views:

253

answers:

8
$myq = sprintf("select user from table where user='%s'", $_POST["user"]);

I would like to know if the above query can be exploited using SQL injection. Is there any advanced SQL injection technique that could break sprintf for this particular query?

+14  A: 

I don't think it needs to be particularly advanced... try an input of

' OR 1 = 1 OR user='

In other words, you'll get SQL of:

select user from table where user='' OR 1 = 1 OR user=''

Does that look like a query you really want to execute? (Now consider the possibility of it dropping tables instead, or something similar.)

The bottom line is that you should be using a parameterised query.

Jon Skeet
doesnt sprintf escape single quotes?
No, it does not. At least not in my experience with PHP.
Jordan S. Jones
@user294924, %s tells the printf subsystem to print whatever string the variadic arguments contain. It does no checking, no filtering, no escaping .. it does exactly what you tell it to do.
Tim Post
Its particularly important to paramaterize the query. Also test your input for valid characters. (note that I did not say invalid since that will eventually cause programming problems as the attacks get more specific)
Dave
@Downvoter: Care to give a reason?
Jon Skeet
@Jon Skeet There is a cult that ritually down votes anyone who forces a puppet (aka Tony) into slavery. I think its called (P)eople (H)elping (P)uppets.
Tim Post
Care of one single downpoint of 150k? OMG
Col. Shrapnel
@Col. Shrapnel: Its not the down vote that bothers most of us, its the fact that someone clicked a button saying "That answer is not good" without offering suggestions on how it might be wrong or how to better word it. It helps everyone if a comment is left for any kind of vote, up or down because it helps people learn and write better answers. The 'pundit' badge exists to encourage people to leave informative comments.
Tim Post
@Col. Shrapnel: I don’t think it’s about the -2 reputation. It’s rather to get a response about what’s wrong with the answer to improve it.
Gumbo
Not with the answer but with it's votes. This answer's just overvoted. I've never seen such a voting under php tag for the last few days. Much more informative answers to the much more complicated questions take 3-4 max. I wonder what's so special with this one.
Col. Shrapnel
@Col. Shrapnel: Gumbo is spot on. A downvote usually means that either *I've* misunderstood something or the downvoter has. In both cases, there's something to be learned, but only if the vote is explained.
Jon Skeet
@Col. Shrapnel: When people see `Jon Skeet', they tend to up vote. Discovering the dynamics of fame is an exercise left for the reader. However, down voting something _just because_ Jon Skeet wrote it and received lots of votes is equally as stupid as up voting something just because Jon Skeet wrote it. If his answer was incorrect, I would have down voted and commented (or just edited and fixed it). Behold the beauty of SO.
Tim Post
You guys take it too seriously. Take it easy. It's just numbers.
Col. Shrapnel
@Col. Shrapnel: You're missing the point. It's not about the numbers at all. Obviously I don't care about 2 reputation - that wouldn't be worth mentioning. But a downvote is usually a sign of disagreement, and that *is* significant. If you treat SO as just a numbers game, you miss out on a huge chance to learn from other people.
Jon Skeet
"The bottom line is that you should be using a parametrized query." Agree 100%. And SQL injection is not the only reason. At least in Oracle you should bind variables where possible for performance reason also. That way DB can reuse previously parsed queries and calculated execution plans and skip locking that is necessary in the execution plan calculation process.
Petar Repac
@Petar Repac in the web environment we have very different conditions. Another connection being established with each request. And also queries take not hundreds of kilobytes but merely bytes - so, no big deal with parsing. Actually prepared statements have no benefit in performance over ol'good escaping.
Col. Shrapnel
@Jon Skeet: yes, it was a sign of disagreement. I disagree with overvoting for such a banal answer. And it *is* numbers. There are always some noise and observational errors. Try to learn not to take each downvote too personal :)
Col. Shrapnel
Not just a numbers game? Tell it to ones who speaks of "discovering the dynamics of fame", cults and such :P But not to me. O.k.?
Col. Shrapnel
@Col. Shrapnel: You seem to still be deliberately missing my point. Do I enjoy having the top rep? Yes. Does that mean that two points matter to me? No. Do I still care if someone thinks I've written something wrong? Absolutely. I don't see why that's difficult to understand.
Jon Skeet
yeah, I see your point. Now watch mine. As I learned from your admirers, not only your post can affect voting, but just your name. Not it's meaning but just name. As I suspect, you don't care too much for excessive up voting, don't take it as a sign of perfectness, do you? So, be consistent, don't take down wotes as a sign of wrongness. You're just aside from such things. That's disadvantage of being at the top.
Col. Shrapnel
@Col. Shrapnel: If I didn't take downvotes as a cause for concern, that would be tantamount to assuming I was always right. No, I will continue asking for reasons when I'm downvoted, to encourage people to express their disagreements. In this case there may not be anything technical, but sometimes there is - and that's valuable to me.
Jon Skeet
it makes sense. your admirers wronged me first. I hope you take my explanations as well. Nothing bad If there is no comment followed a vote. Just curious, it SO folks goes to implement a feature, when @ comment to a special nickname, "Downwoter" goes to be shown to the recent downvoter as a personal comment?
Col. Shrapnel
@Col. Shrapnel: Now that's a nice idea. It keeps things anonymous (which I can see the value of) but still gets the point across. Obviously there'd have to be a hard-wired exception for users called "Downvoter" but hey :) Fancy proposing it on Meta?
Jon Skeet
@Col. Shrapnel: It doesn't matter if it's web app or not. I think that parsed queries are not binded to db connection anyway. Way would DB throw away something valuable when 1 connection ends. There is often high probability that another connection will issue the same query (e.g. another user on the same page). And query size doesn't matter also. Another thing is that making a DB connection is expensive and usually connections are taken from connection pool or shared.
Petar Repac
+4  A: 

when $_POST["user"] would equal "';SHUTDOWN;" - what would happen?

Axarydax
sprintf would have escaped single quotes and the query sent to db would be select user from table where user='\';SHUTDOWN;'
If the poster doesn't use mysqli_multi_query, nothing will happen because all other mysql query methods in PHP only execute one query.
chiborg
A: 
$_POST["user"] = "' or 1=1 or user='"
Joe Philllips
A: 

Yes.

If somebody put in the following as the user in your form:

'; delete * from table
Justin Niessner
Though it won't work with usual mysql, it's good for example.
Col. Shrapnel
+6  A: 

Using sprintf doesn’t give you any more protection than using simple string concatenation. The advantage of sprintf is just having it a little more readable than when to using simple PHP’s string concatenation. But sprintf doesn’t do any more than simple string concatenation when using the %s format:

$str = implode('', range("\x00", "\xFF"));        // string of characters from 0x00 – 0xFF
var_dump(sprintf("'%s'", $str) === "'".$str."'"); // true

You need to use functions that escape the contextual special characters you want to insert your data into (in this case a string declaration in MySQL, supposing you’re using MySQL) like mysql_real_escape_string does:

$myq = sprintf("select user from table where user='%s'", mysql_real_escape_string($_POST["user"]));
Gumbo
+4  A: 

alt text

Yes, I'd say you have a potential problem there :)

You need to escape: \x00, \n, \r, \, ', " and \x1a. sprintf() does not do that, sprintf() does no modification to strings, it just expands whatever variadic arguments that you give it into the buffer that you provide according to the format that you specify.

If the strings ARE being transformed, its likely due to magic quotes (as Rob noted in Comments), not sprintf(). If that is the case, I highly recommend disabling them.

Tim Post
+1  A: 

Ahh here I come with the magic answer! :)
magic quotes do escaping for you!

So, you have to turn magic_quotes_gps ini directive off
and then use mysql_real_escape_string as suggested.

Col. Shrapnel
A: 

Actually, turn off magic quotes.

In PHP, where it's appropriate, use filters:

$inUser = $_POST['user'];
$outUser = filter_var($inUser, FILTER_SANITIZE_STRING);

Filters strip out HTML tags and escape various characters.

In addition, you can let your database escape it for you:

$inUser = $_POST['user'];
$outUser = mysqli_real_escape_string($conn, $inUser);

This escapes MySQL specific special characters like double quotes, single quotes, etc.

Finally, you should use parameterized queries:

$sql = "SELECT user FROM table WHERE user = ?";
$stmt = $pdo->prepare($sql);
$params = array($outUser);
$stmt->execute($params);

Parameterized queries automatically add the quotes around strings, etc., and have further restrictions that make SQL injections even more difficult.

I use all three, in that order.

Marcus Adams