Sun, 16 Jul 2006
PHP Security
While I was at KTD, Jack showed me the upcoming KTD website. Because they're going to be taking reservations through the web, I offered to point him to some articles on PHP security. Since what I found is of general interest, I thought I'd post it to the web.
Here's a short introduction to web application security, with a focus on PHP. I have to warn you that I'm not that familar with PHP, the examples are untested and based on what I read in the PHP manual. But the principles are the same as in any other programming language.
The idea is that you have you have a web application that has permission to do the work it needs to run the application. It uses the server's operating system, reads and writes files, reads and writes to the database, and builds web pages to display to the user. The person attacking the security of your site tries to get your application to do what you didn't expect or want it to by supplying input to your application that you never expected. The solution to the problem is to restrict the input that your application gets by carefully checking it and rejecting or converting input that causes trouble.
There are several standard routes of attack that are used to subvert web applications. Your application can be tricked into running a command you didn't want it to. It can be tricked to write (or overwrite) a file you did not expect. It can be tricked to execute an sql command you don't want. (This is called an sql injection attack.) Or the web page your application builds can contain Javascript code that steals information from your interaction with the person running it. (The usual target is the session id cookie. This is known as a cross site scripting attack.)
This article gives a good introduction to common PHP security mistakes. Many are obvious, but it's surprising how often these problems are overlooked. This weblog post covers some of the same ground, but is short and makes some different points.
I'm going to focus on sql injection attacks. The two articles above both cover sql injection attacks. But to get more familiar with the technique, it would be good to read SQL Injection Attacks by Example, which works out a example attack on a web application.
The way to protect yourself is to take the input fields that are going to be used in your sql command. Divide them into fields that should have numeric values and string values. Use is_numeric() or is_int() to check the numeric values and reject them if they fail the test. Here are the PHP manual pages for these two functions.
To protect the string fields, use the function mysql_real_escape_string(). This function escapes characters that could be used to subvert your application by preceding them with backslashes. Here is the manual page describing this function.
Using this function is a little tricky, because of the PHP "magic quotes" feature also adds backslashes that escape some, but not all of the characters that mysql_real_escape_string() escapes. If you use this function and magic quotes are turned on, you'll wind up with double slashes in your input, where the same character has been escaped twice. You may or may not be able to turn off the magic quotes depending on your ISP. But what you can do is check to see if the feature is turned on and undo the damage it causes by stripping the backslashes that it adds. That's what the smart_quotes() function in Example 3 on the manual page does. The function looks fine to me, but I wouldn't rely on it to detect the difference between string and numeric fields. Some string fields, like zip codes look like numbers and you shouldn't rely on a function to try to guess if a field a string or numeric based on the user's input. The comments at the end of the manual page raise this point. Test numeric fields separately, as I suggested.
