As the Web continues its march towards becoming the de facto interface for the world’s software applications, developers must find effective ways to not only communicate with server processes such as MySQL, but also other operating system tools such as a shell or Ruby script. In this tutorial, I’ll show you how to securely execute a variety of system-based commands via a PHP script, demonstrating how to build web applications that can tightly integrate with both the operating system and third-party software.
Minding the Security Risks
Hopefully, you understand how important it is to thoroughly validate all user input accepted through a web form; after all, if you neglect this task, your data could be stolen or damaged via a SQL injection attack, or your users could be spoofed via a cross-site scripting attack.
The risks of not properly vetting operating system commands initiated through a PHP script are no less severe! With this in mind, before jumping into a few examples it’s worth taking a moment to understand how this particular feature can pose a significant security risk to not only your web site, but also to the stability of the entire web server.
When PHP is installed as an Apache module, it operates on behalf of the system user (which has assumed ownership of the Apache daemon). This means that when PHP executes an operating system command, the Apache daemon owner carries out that command. Therefore, any permissions assigned to the Apache daemon owner are applied when executing this command. Some improperly configured servers actually run Apache using the root user, meaning an errant PHP script could conceivably delete large parts of the operating system and any data stored within! Even in the more likely case of your server operating on behalf of a non-root user, the security risks remain nonetheless severe.
To illustrate this risk, consider that (as you’ll soon learn) a PHP script is capable of executing a command that can read a file residing on the operating system, including those residing outside of the web document root. Suppose you’re working as a software developer for a major university, and one day your supervisor asks you to create a web-based script that allows a student to retrieve information that verifies whether his or her student fee payment has been processed. This script would work by passing the student’s social security number along as an argument to a command-line script, which in turn would communicate with a university mainframe. That command-line script would be executed like this:
Your PHP script doesn’t actually bother to verify whether the student input is a syntactically valid SSN, much less ensure that the SSN isn’t accompanied by other input that could produce an unintended result, such as retrieving the server’s
/etc/passwdfile (the file that contains the server’s list of user accounts):
%>/home/mainframe/fees.rb 123-45-6789 ; cat /etc/passwd
If your PHP script were simply retrieving and displaying the returned output from presumably the
fees.rbscript, then this output would also include a list of all user accounts! Obtaining this list could eventually lead to an intruder surreptitiously entering the operating system due to a weak or missing account password. Even worse, if the web server daemon happened to be owned by the root user, then the intruder couldeasilymodify or delete not only
/etc/passwd, but also any other file residing on the server!
Clearly, you should avoid such gaffes at all costs. Thankfully, it’s easy to avoid these sorts of security problems using native PHP syntax. I’ll explain how in the remainder of this tutorial.