The problem here is that we didn't call the C API's mysql_set_charset() instead of SET NAMES. However, be aware that PDO will silently fallback to emulating statements that MySQL can't prepare natively: those that it can are listed in the manual, but beware to select the appropriate server version). the data being sent over in a separate packet from the query). This will usually result in a true prepared statement (i.e. Now, it's worth noting that you can prevent this by disabling emulated prepared statements: $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false) This part is just a formality, but here's the rendered query: SELECT * FROM test WHERE name = '縗' OR 1=1 /*' LIMIT 1Ĭongratulations, you just successfully attacked a program using PDO Prepared Statements. Which is exactly what the attack requires.
#Php pdo prepared statements free
Therefore the call to mysql_real_escape_string() inserts the backslash, and we have a free hanging ' character in our "escaped" content! In fact, if we were to look at $var in the gbk character set, we'd see: 縗' OR 1=1 /* We did tell the server we're using gbk, but the client still thinks it's latin1. However, up to this point, the client thinks that we're still using latin1 for the connection, because we never told it otherwise. So it can perform the escaping properly for the character set that the server is expecting. The C API call to mysql_real_escape_string() differs from addslashes() in that it knows the connection character set. Therefore, PDO internally builds the query string, calling mysql_real_escape_string() (the MySQL C API function) on each bound string value. The important thing to realize here is that PDO by default does NOT do true prepared statements. Or in other words, a valid character followed by an unescaped '. So we'd wind up with 0xbf5c27, which in gbk is a two character sequence: 0xbf5c followed by 0x27. We have chosen this payload because, if we called addslashes() on it, we'd insert an ASCII \ i.e. Note that in latin1 and gbk, 0x27 on its own is a literal ' character. In gbk, that's an invalid multibyte character in latin1, it's the string ¿'.
The payload we're going to use for this injection starts with the byte sequence 0xbf27. There is another way of doing it, but we'll get there soon enough. This sets the character set ON THE SERVER. Now, it's very important to note the use of SET NAMES here. As it turns out, there are 5 such encodings supported in MySQL 5.6 by default: big5, cp932, gb2312, gbk and sjis. 0x27 and to have some character whose final byte is an ASCII \ i.e.
Selecting a Character Set $pdo->query('SET NAMES gbk') įor this attack to work, we need the encoding that the server's expecting on the connection both to encode ' as in ASCII i.e. In certain circumstances, that will return more than 1 row. $query = 'SELECT * FROM test WHERE name = ? LIMIT 1' So, let's start off by showing the attack. It's based off an attack demonstrated here. I'm adapting this answer to talk about PDO. Preparing the statement protects against injection because the query and the data are sent separately and your database engine is expecting only specific data for your "placeholders".The short answer is NO, PDO prepares will not defend you from all possible SQL-Injection attacks. What you are doing is just inserting $searchterm directly into the query which is not protecting against anything.Įssentially the same as doing $query ="SELECT id FROM blah WHERE name LIKE '%$searchterm%'" I don't think that prepare statement is doing what you think is, unless there is an alternate syntax that I am unaware of.įrom my understanding to actually prepare variables you need to put a place holder into your prepare statement and call bind param to actually bind the param, by doing that you are protecting against any injection.įor example: $sql = "SELECT id FROM blah WHERE name LIKE :searchTerm"