Dynadot

Traverse Directories the Easy Way with Glob() !

Spaceship Spaceship
Watch
Impact
4
For the original article along with many more PHP discussions, please visit TalkPHP.com.

Sprintf in itself will not secure a MySQL query from head to toe. That should be made clear from the word go. There are many more techniques that go into ensuring a MySQL statement is safe to execute on the MySQL server. Sprintf will, however, take a lot of the sting out of any malice.

Not only will your new found knowledge help you when it comes to security, but sprintf and its twin brother with a slight genetic mismatch, printf, are on hand to make your PHP code look a lot more programmer-friendly.

Gaping Security Hole

Take the following snippet of PHP code. Although I have hard-coded the $iColumnId variable, assume that it is expecting an integer value, in our case 5, from a user form.

PHP:
$iColumnId = '5 OR id != 0';

$szSQL = "  DELETE FROM
				myTable
			WHERE
				id = " . $iColumnId;

As we were allowed to specify more than the ID, we have effectively crafted ourselves a harmful query. The very reason this is allowed to happen is because the variable is simply concatenated (joined) onto the query and assumed safe. Our query now reads as the following bad, bad query:

DELETE FROM myTable WHERE id = 5 OR id != 0

In pseudo-terms this reads delete everything from myTable where the id column equals 5 or doesn't equal 0. What does this mean? Well, it means that the individual who was able to append data to our query has essentially deleted every single record from our entire table. Don't scream!

The Solution

Enter sprintf. Sprintf could have saved us from the nasty surprise of awakening to find an empty table. Sprintf could have been your friend! Albeit sprintf does not have any magical reverse procedure, nor does it even care that your entire table is no more. Sprintf would have cared though if you'd had paid a little more attention to it before placing the insecure file live. Take the following example again but this time we're using sprintf:

PHP:
$iColumnId = '5 OR id != 0';

$szSQL = sprintf("  DELETE FROM
						myTable
					WHERE
						id = %d",
					$iColumnId);

There we have it! Sprintf has just saved your life, your table, your dignity as well as your credibility. I'm sure those 4 points aren't mentioned in the function's documentation but they are nonetheless byproducts of such simplicity!

As we are expecting an ID we have specified to sprintf that the argument should be an integer and nothing more. This is very much the same as typecasting which is a topic we'll get into in more depth in a couple of days' time. Notice the %d, sprintf has many of these to specify the type of data you are expecting. In our example sprintf has cleverly noticed we wanted an integer and converted our malicious string into an integer. Thus leaving us with:

DELETE FROM myTable WHERE id = 5

It has withered the string down to make it an integer. As our string began with an integer, it was able to remove all the string data. Ingenious!

Incidentally: Although a user would be able to specify any integer in this instance, please note that if your table consists of data for many users then you will want to add another WHERE clause to ensure they are unable to delete other users' data. The most common being the following: ...AND member_id = %d

Inner Workings

Sprintf is a nice function in itself, but to any individual who has never approached its live-saving qualities before may find themselves undeniably perplexed. Sprintf works like so:

  • You specify the first argument which is the string you want to use. In our case this is a MySQL statement but it could be just about anything.
  • You enter the %d and %s etc. where you want the variables from the arguments to appear based on your desired variable types.
  • PHP, upon execution, reads the statement. Every time it finds a %x where x could be anything it uses the respective argument (the first %x corresponds to the 2nd argument, the second %s corresponds to the 3rd argument - and so on.

To elucidate this somewhat, if I had the following:

PHP:
$szCats = 'cats';
$szDogs = 'dogs';
printf("Raining %s and %s.", $szCats, $szDogs);

This will leave us with the following string:

Raining cats and dogs.

But if I reversed the variables as they appear in the 2nd and 3rd arguments of our printf function (note: printf is exactly like sprintf except the function's output is echoed and not returned) to be like so:

PHP:
$szCats = 'cats';
$szDogs = 'dogs';
printf("Raining %s and %s.", $szDogs, $szCats);

It would leave me with the nonsensical idiom:

Raining dogs and cats.

Type Specifiers

Type specifiers are our odd looking %x (where x is anything). These specify the types of data we desire in that position in our 1st argument. In the earlier example we wanted only integers so we specified %d, but in our cats and dogs example we wanted 2 strings so we set them both as %s.

Sprintf and its brother printf support the following type specifiers (taken straight from php.net):

  • % - a literal percent character. No argument is required.
  • b - the argument is treated as an integer, and presented as a binary number.
  • c - the argument is treated as an integer, and presented as the character with that ASCII value.
  • d - the argument is treated as an integer, and presented as a (signed) decimal number.
  • e - the argument is treated as scientific notation (e.g. 1.2e+2). The precision specifier stands for the number of digits after the decimal point since PHP 5.2.1. In earlier versions, it was taken as number of significant digits (one less).
  • u - the argument is treated as an integer, and presented as an unsigned decimal number.
  • f - the argument is treated as a float, and presented as a floating-point number (locale aware).
  • F - the argument is treated as a float, and presented as a floating-point number (non-locale aware). Available since PHP 4.3.10 and PHP 5.0.3.
  • o - the argument is treated as an integer, and presented as an octal number.
  • s - the argument is treated as and presented as a string.
  • x - the argument is treated as an integer and presented as a hexadecimal number (with lowercase letters).
  • X - the argument is treated as an integer and presented as a hexadecimal number (with uppercase letters).

As you can clearly see from the above enumeration, sprintf and printf support a legion of type specifiers. These are all there to save you from the otherwise impending doom of insecure scripts that are susceptible to the notorious SQL injection attacks. They should really be used on a common basis! Somebody breaching your PHP script's security has to be a rather embarrassing situation to be in.

Last but certainly not least, sprintf supports functions inside functions, so to make your MySQL statement even more secure, you could do the following:

PHP:
$szSQL = sprintf("  DELETE FROM
						myTable
					WHERE
						id = %s",
					mysql_real_escape_string($iColumnId));

Which will escape our string at the same time as ensuring it is a string. This prevents any harmful characters being taken literally.

All in all and security aside, I'm sure the majority of you also agree that using the sprintf and printf functions make your PHP look a lot prettier. Without it I'm confident every PHP script would look similar to Britney Spears (with a bald head or a wig, both are equally as unsightly, right?)
 
0
•••
The views expressed on this page by users and staff are their own, not those of NamePros.
  • The sidebar remains visible by scrolling at a speed relative to the page’s height.
Back