Executing a System Command
PHP actually supports several functions capable of executing a system command, including
exec()
, passthru()
, shellexec()
, and system()
. Each of these functions exhibits a slightly different behavior:exec()
: This function executes an external command, optionally saving the output to an array.passthru()
: This function executes an external command, displaying the raw output directly to the browser.shellexec()
: This function executes an external command, saving the output to a string.system()
: This function executes an external command, flushing the web server’s output buffer after each line of output is returned.
Let’s review a few examples involving these functions, beginning with the particularly simple task of executing the
ls
command in order to output a list of files residing in a particular directory:
<?php
exec("ls -al /home/wjgilmore/pdfs", $lines);
foreach($lines as $line) {
echo "{$line}<br />";
}
?>
Executing this script produces output identical to what you would see if executing
ls -al /home/wjgilmore/pdfs
from the command-line:
total 20
drwxr-xr-x 2 wjgilmore wjgilmore 4096 Jan 25 13:01 .
drwxr-xr-x 4 wjgilmore wjgilmore 4096 Jan 25 13:02 ..
-rw-r--r-- 1 wjgilmore wjgilmore 1366 Jan 25 13:00 01-2010.pdf
-rw-r--r-- 1 wjgilmore wjgilmore 1263 Jan 25 13:01 02-2010.pdf
-rw-r--r-- 1 wjgilmore wjgilmore 685 Jan 25 13:01 03-2010.pdf
Of course, there’s nothing particularly insecure about this example because the system command has been hardcoded as an
exec()
parameter. However, let’s return to the earlier scenario where a user was allowed to key in a Social Security Number, which was subsequently provided to a Ruby script. The user would presumably provide this input via a web form, with the SSN passed via the $_POST
array. Coded in an insecure fashion, the script might look like this:
<?php
$ssn = $_POST['ssn'];
exec("/home/mainframe/fees.rb {$ssn}", $lines);
foreach($lines AS $line) {
echo "{$line}<br />";
}
?>
To demonstrate the danger of this script, consider the following
fees.rb
script, which just outputs the SSN back to the user:
#!/usr/bin/ruby
ssn = ARGV[0]
puts "The SSN is #{ssn}n"
Now, suppose the user entered the following “social security number” into the form:
123-45-6789 ; cat /etc/passwd
Providing this input to the PHP script results in the following output:
The SSN is 123-45-6789
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
...
Surely this isn’t the output you intended!
Securing Your Scripts
Fortunately, several options are at your disposal for preventing such unintended consequences. The easiest solution involves using the
escapeshellarg()
and escapeshellcmd()
functions to convert the user-supplied data into a safe format. The escapeshellarg()
function will delimit the data with single quotes, as well as any single quotes already found in the data, thereby causing the data to be treated as a single argument. Therefore, the above malicious input would be converted to this:
'123-45-6789 ; cat /etc/passwd'
Once delimited, the PHP script will now produce the following output:
The SSN is 123-45-6789 ; cat /etc/passwd
The
escapeshellcmd()
function will escape any characters that could be used to trigger a system command. If applied to the above user-supplied data, the string will be converted to the following before being passed to the Ruby script:
123-45-6789 ; cat /etc/passwd
With the semi-colon escaped, the ensuing command can no longer be interpreted by the operating system.
Where to From Here?
Creating web-based applications that integrate tightly with the underlying operating system is pretty easy to do. However, you must be vigilant to avoid the serious security issues that can arise due to unchecked user input. Fortunately, PHP’s native functionality makes it easy to vet user input in a way that greatly reduces the likelihood of stolen or damaged server data.
W. Jason Gilmore is founder of EasyPHPWebsites.com. He is the author
of several popular books, including “Easy PHP
web sites with the Zend Framework,” “Easy PayPal with PHP,” and “Beginning
PHP and MySQL, Third Edition.” to e-mail him.
of several popular books, including “Easy PHP
web sites with the Zend Framework,” “Easy PayPal with PHP,” and “Beginning
PHP and MySQL, Third Edition.” to e-mail him.