Secure PHP Programming 101
By Michael McCann
Last Updated: January 10, 2008
Writing insecure code is easy. Everybody does it. Sometimes we do it accidentally because we don?t realize that the security issue exists, and sometimes we do it on purpose because we suspect the bad guys won?t notice one little vulnerability. Secure programming is often overlooked because of ignorance, time constraints, or any number of other factors. Since security isn?t flashy until something goes wrong, it is often easy put it off.
Once your application is compromised, you will realize there?s nothing more important. The best case scenario is that you lose days of productivity and suffer downtime while you fix what was damaged. The worst case scenario &em; your data is compromised and you have no idea if it is correct, much less what the hackers managed to copy and read. Did you expose usernames and passwords to the world? Did you happen to release the credit card information for thousands into the den of identity thieves? You?ll never really be able to know. It?s best to practice secure programming so you never need to ask yourself these questions.
With this in mind, let?s examine three different classes of secure programming "no-noes," storage risks, system risks, and exposure risks and discuss how we can prevent each of them. Server configuration and data transmission security are beyond the scope of this article, but the reader should be aware that they also play a major role in securing a web application.
Storage risks are those risks involved in the storing data and interacting with a database server or file system. The most widely known of these in the infamous SQL injection attack. SQL injection is when you allow the user to input data into a query, and instead of a value he adds his own SQL into the query. The easiest way to prevent this type of attack is to escape every user variable that could touch your queries. Luckily, PHP has several build in functions for handling this, such as mysql_escape_string(). Essentially, this works by escaping characters in a string that could conceivably be used to terminate your query and run a user specified query.
When should you escape user data? It all depends on who you talk to. Some programmers prefer to escape as soon as it enters the application, while others prefer to wait until just before it is placed into the query. Personally, I prefer to escape right before it is inserted into the query. I do this because I can always look at the code, see the database interaction, and see that the data was escaped before it was being used. I don?t need to search the entire source to make sure something was escaped.
The second storage risk we?ll talk about is storing passwords as plain text (hereafter referred to as clear text). I know you guys do it; I?ve seen too many open source applications and too many in-house applications to believe that it doesn?t go on. Simply put, there is never any reason to store a password in clear text. It doesn?t matter if you?re storing the password in a database or a flat file, always store passwords as a hash. You can accomplish this simply enough by using PHP?s md5() function to transform the password before you insert it into your storage medium. Since md5 is repeatable, you can validate a password by simply using
When should you transform the password to a hash? You should do it as soon as possible. Don?t let the password variable float around your application at all. As soon as you grab the password input, convert it into a hash. I prefer to do this by setting the password variable to its own hash, this avoids the chance of using the wrong variable in later code.
Next, let?s talk about the usernames and passwords your program needs in order to interact with other applications (like database servers). You should always separate these out into a different PHP file than the rest of your code, and reference them as constants or variables. This not only makes your code easier to maintain (if you need to change a password, you know exactly where to look), it the event that your source gets released, you know that the password isn?t in that file. While it?s certainly true that they could grab your password file, it does reduce the risk considerably.
Before we leave usernames behind, I want to touch on the concept of division of power. We?re not talking about the government in this case, but about database users. The database user accounts your program uses should have the minimum level of access they need in order to function correctly.
If your application only reads from a database, then the database account it uses should only have SELECT permission on that particular database, and no access to any other database.
To take this concept a step further, I prefer to create multiple database accounts for my web applications. Typically I create one account that only has INSERT permissions for the particular tables the software needs to write to, and a completely separate account that only has SELECT access. This makes sure that no INSERT queries are accidentally performed and mitigates the possible damage done by SQL injections.
Of course, multiple accounts work best when there?s a clear separation between those who can write to a database and those who can read it (such as a CMS). In theory, you could use multiple accounts in any application but you run into problems with the number of open connections to the database. This is simply something that should be considered as a possibility during the design phase of your software.
I?m a big advocate, as are most programmers, of breaking source code down into multiple files at every logical opportunity. However, I?ve noticed that a lot of PHP programmers have a nasty habit of naming PHP files they intend to use as libraries or other include types with the extension .inc, or .config, or some other non .php extension. This is a horrible idea because the server its running on might not be setup to parse these extensions as PHP files, so anyone loading the file would be exposing their source code (and potentially passwords, usernames, and other protected information) to the world. I prefer to prefix filenames myself, using inc_ or class_ when needed.
While we?re discussing included files, I would like to talk about to other security precautions. If you have a PHP file that you intend to use only as part of a larger PHP application, add this line to the beginning of the file (__FILE__,