For better or worse, there are a lot of novice programmers hammering away at PHP scripts all over the planet. It is one of the most common web scripting languages. However, it’s simply too hard for a newbie PHP programmer to write secure PHP code. As I’ll demonstrate, it’s also impossible for even security minded PHP professionals to keep their applications secure due to the way PHP manages change to its ever-growing API. Their culture of “add stuff, but stuff the security implications” has to stop. Don’t get me wrong, I love change. I just don’t love the way the PHP project goes about it.
Let’s take a non-hypothetical instance. Some functions are very familiar to Unix folks, like fopen(), fread(), fclose() and so on. In Unix, the semantics of these functions and the security issues surrounding them are well understood. However, in PHP, fopen() and friends are heavily overloaded, and gain new functionality between even minor PHP releases. For example, by default, PHP’s fopen() and several friends can open any file on the Internet. Producing a canonical filename which is safe is basically impossible in PHP.
Take a typical PHP application using templated languages. A typical implementation will enumerate a directory to see what files are available (English.lang.php, Ñ€ÑƒÑÑÐºÐ¸.lang.php, etc) and then try to “fix” it up. The attacker will then try to substitute ../../../../../etc/passwd or something similar. Nothing new here for our Unix friends. But what about going offsite? Well, the top vulnerability for PHP applications in 2005 is remote file inclusion and it uses this exact same mechanism.
The usual type of thing I see all the time:
$language = $_POST[‘language’] + “.lang.php”;
Of course, the security people reading this are going “nononononno!”. But to the average PHP programmer, why should it be any harder? PHP just made a basic idea very hard to get right. This is not to say J2EE or ASP.NET are invulnerable to this type of boneheaded programming, but they don’t allow you to include files from over the Internet and then evaluate their contents.
What about if we move to file_get_contents() instead of including the result? file_get_contents is rarely used as it is a PHP 4.3.0 and later construct, and PHP coders are reluctant to use new fangled calls when old ones will do. However, it is no better! It STILL allows us to read the file directly from a URL or via a wrapper, like php://output (which acts like echo… with the usual association of data… XSS city), or php://filter/resource=http://www.example.com … and this is NOT restricted by allow_url_fopen. Who comes up with these settings?
Programmers are usually surprised at the wide number of places what used to be local file operations are able to be used for remote file and file filters. The job is made harder because PHP keeps on changing its mind about what is available. What used to be a safe application with PHP 4.2.x is no longer safe in PHP 4.3.x or PHP 5 – just because PHP changed.
Accompanied by extremely fragmented documentation (ie “see Appendix L” or read the usually extensive comments to see how the functions ACTUALLY work), it takes experience to program PHP’s file operations. With a very low barrier of entry, PHP needs to keep these advanced features to those who know what they’re doing. However, it’s far too late. PHP is used by programmers of many different skill levels. The average Joe programmer has no help in hell of writing a safe PHP application.
In the meantime, let me plug Chris Shiflett’s brand spanking new PHP Security book from O’Reilly:
If you want to write secure apps in PHP, you need that book.