colder Tue Apr 17 16:31:00 2007 UTC
Modified files: /phpdoc/en/security filesystem.xml Log: Improve filesystem's security man page http://cvs.php.net/viewvc.cgi/phpdoc/en/security/filesystem.xml?r1=1.3&r2=1.4&diff_format=u Index: phpdoc/en/security/filesystem.xml diff -u phpdoc/en/security/filesystem.xml:1.3 phpdoc/en/security/filesystem.xml:1.4 --- phpdoc/en/security/filesystem.xml:1.3 Sun Aug 8 16:11:36 2004 +++ phpdoc/en/security/filesystem.xml Tue Apr 17 16:31:00 2007 @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="iso-8859-1"?> -<!-- $Revision: 1.3 $ --> +<!-- $Revision: 1.4 $ --> <!-- splitted from ./index.xml, last change in rev 1.66 --> <chapter id="security.filesystem"> <title>Filesystem Security</title> @@ -34,16 +34,19 @@ <?php // remove a file from the user's home directory $username = $_POST['user_submitted_name']; -$homedir = "/home/$username"; -$file_to_delete = "$userfile"; -unlink ("$homedir/$userfile"); -echo "$file_to_delete has been deleted!"; +$userfile = $_POST['user_submitted_filename']; +$homedir = "/home/$username"; + +unlink("$homedir/$userfile"); + +echo "The file has been deleted!"; ?> ]]> </programlisting> </example> - Since the username is postable from a user form, they can submit - a username and file belonging to someone else, and delete files. + Since the username and the filename are postable from a user form, + they can submit a username and a filename belonging to someone else, + and delete it even if they're not supposed to be allowed to do so. In this case, you'd want to use some other form of authentication. Consider what could happen if the variables submitted were "../etc/" and "passwd". The code would then effectively read: @@ -54,11 +57,13 @@ <?php // removes a file from anywhere on the hard drive that // the PHP user has access to. If PHP has root access: -$username = "../etc/"; -$homedir = "/home/../etc/"; -$file_to_delete = "passwd"; -unlink ("/home/../etc/passwd"); -echo "/home/../etc/passwd has been deleted!"; +$username = $_POST['user_submitted_name']; // "../etc" +$userfile = $_POST['user_submitted_filename']; // "passwd" +$homedir = "/home/$username"; // "/home/../etc" + +unlink("$homedir/$userfile"); // "/home/../etc/passwd" + +echo "The file has been deleted!"; ?> ]]> </programlisting> @@ -86,23 +91,27 @@ // removes a file from the hard drive that // the PHP user has access to. $username = $_SERVER['REMOTE_USER']; // using an authentication mechanisim +$userfile = basename($_POST['user_submitted_filename']); +$homedir = "/home/$username"; -$homedir = "/home/$username"; +$filepath = "$homedir/$userfile"; -$file_to_delete = basename("$userfile"); // strip paths -unlink ($homedir/$file_to_delete); - -$fp = fopen("/home/logging/filedelete.log","+a"); //log the deletion -$logstring = "$username $homedir $file_to_delete"; -fwrite ($fp, $logstring); +if (file_exists($filepath) && unlink($filepath)) { + $logstring = "Deleted $filepath\n"; +} else { + $logstring = "Failed to delete $filepath\n"; +} +$fp = fopen("/home/logging/filedelete.log", "a"); +fwrite($fp, $lo gstring); fclose($fp); -echo "$file_to_delete has been deleted!"; +echo htmlentities($logstring, ENT_QUOTES); + ?> ]]> </programlisting> </example> - However, even this is not without it's flaws. If your authentication + However, even this is not without its flaws. If your authentication system allowed users to create their own user logins, and a user chose the login "../etc/", the system is once again exposed. For this reason, you may prefer to write a more customized check: @@ -111,14 +120,16 @@ <programlisting role="php"> <![CDATA[ <?php -$username = $_SERVER['REMOTE_USER']; // using an authentication mechanisim -$homedir = "/home/$username"; - -if (!ereg('^[^./][^/]*$', $userfile)) - die('bad filename'); //die, do not process +$username = $_SERVER['REMOTE_USER']; // using an authentication mechanisim +$userfile = $_POST['user_submitted_filename']; +$homedir = "/home/$username"; + +$filepath = "$homedir/$userfile"; + +if (!ctype_alnum($username) || !preg_match('/^(?:[a-z0-9_-]|\.(?!\.))+$/iD', $userfile)) { + die("Bad username/filename"); +} -if (!ereg('^[^./][^/]*$', $username)) - die('bad username'); //die, do not process //etc... ?> ]]>