< Day Day Up > |
10.7 Sanitizing Externally Supplied FilenamesJust like data submitted in a form or URL can cause problems when it is displayed (cross-site scripting attack) or put in an SQL query (SQL injection attack), it can also cause problems when it is used as a filename or as part of a filename. It doesn't have a fancy name like those other attacks, but it can be just as devastating. The cause of the problem is the same: there are special characters that must be escaped so they lose their special meaning. In filenames, the special characters are / (which separates parts of filenames), and the two-character sequence .. (which means "go up one directory" in a filename). For example, the funny-looking filename /usr/local/data/../../../etc/passwd doesn't point to a file under the /usr/local/data directory but instead to the file /etc/passwd, which, on most Unix systems, contains a list of user accounts. The filename /usr/local/data/../../../etc/passwd means "from the directory /usr/local/data, go up one level (to /usr/local), then go up another level (to /usr), then go up another level (to /, the top level of the filesystem), then down into /etc, then stop at the file passwd." How could this be a problem in your PHP programs? When you use data from a form in a filename, you are vulnerable to this sort of attack unless you sanitize that submitted form data. Example 10-23 takes the approach of removing all forward slashes and .. sequences from a submitted form parameter before incorporating the parameter into a filename. Example 10-23. Cleaning up a form parameter that goes in a filename// Remove slashes from user $user = str_replace('/', '', $_POST['user']); // Remove .. from user $user = str_replace('..', '', $user); print 'User profile for ' . htmlentities($user) .': <br/>'; print file_get_contents("/usr/local/data/$user"); If a malicious user supplies ../../../etc/passwd as the user form parameter in Example 10-23, that is translated into etcpasswd before being interpolated into the filename used with file_get_contents( ). Another helpful technique for getting rid of user-entered nastiness is to use realpath( ). It translates an obfuscated filename that contains .. sequences into the ..-less version of filename that more directly indicates where the file is. For example, realpath('/usr/local/data/../../../etc/passwd') returns the string /etc/passwd. You can use realpath( ) as in Example 10-24: to see whether filenames, after incorporating form data, are acceptable. Example 10-24. Cleaning up a file name with realpath( )$filename = realpath("/usr/local/data/$_POST[user]"); // Make sure that $filename is under /usr/local/data if ('/usr/local/data/' = = substr($filename, 0, 16)) { print 'User profile for ' . htmlentities($_POST['user']) .': <br/>'; print file_get_contents($filename); } else { print "Invalid user entered."; } In Example 10-24, if $_POST['user'] is james, then $filename is set to /usr/local/data/james and the if( ) code block runs. However, if $_POST['user'] is something suspicious such as ../secrets.txt, then $filename is /usr/local/secrets.txt, and the if( ) test fails, so Invalid user entered is printed. |
< Day Day Up > |