mirror of
https://github.com/sigmasternchen/php-doc-en
synced 2025-03-15 16:38:54 +00:00

git-svn-id: https://svn.php.net/repository/phpdoc/en/trunk@336910 c90b9560-bf6c-de11-be94-00142212c4b1
220 lines
7 KiB
XML
220 lines
7 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- $Revision$ -->
|
|
<!-- splitted from ./index.xml, last change in rev 1.66 -->
|
|
<chapter xml:id="security.filesystem" xmlns="http://docbook.org/ns/docbook">
|
|
<title>Filesystem Security</title>
|
|
<simpara>
|
|
<acronym>PHP</acronym> is subject to the security built into most server systems with
|
|
respect to permissions on a file and directory basis. This allows
|
|
you to control which files in the filesystem may be read. Care
|
|
should be taken with any files which are world readable to ensure
|
|
that they are safe for reading by all users who have access to that
|
|
filesystem.
|
|
</simpara>
|
|
<simpara>
|
|
Since <acronym>PHP</acronym> was designed to allow user level access to the filesystem,
|
|
it's entirely possible to write a <acronym>PHP</acronym> script that will allow you
|
|
to read system files such as /etc/passwd, modify your ethernet
|
|
connections, send massive printer jobs out, etc. This has some
|
|
obvious implications, in that you need to ensure that the files
|
|
that you read from and write to are the appropriate ones.
|
|
</simpara>
|
|
<simpara>
|
|
Consider the following script, where a user indicates that they'd
|
|
like to delete a file in their home directory. This assumes a
|
|
situation where a <acronym>PHP</acronym> web interface is regularly used for file
|
|
management, so the Apache user is allowed to delete files in
|
|
the user home directories.
|
|
</simpara>
|
|
<para>
|
|
<example>
|
|
<title>Poor variable checking leads to....</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
// remove a file from the user's home directory
|
|
$username = $_POST['user_submitted_name'];
|
|
$userfile = $_POST['user_submitted_filename'];
|
|
$homedir = "/home/$username";
|
|
|
|
unlink("$homedir/$userfile");
|
|
|
|
echo "The file has been deleted!";
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
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:
|
|
<example>
|
|
<title>... A filesystem attack</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
// removes a file from anywhere on the hard drive that
|
|
// the PHP user has access to. If PHP has root access:
|
|
$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>
|
|
</example>
|
|
There are two important measures you should take to prevent these
|
|
issues.
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>
|
|
Only allow limited permissions to the <acronym>PHP</acronym> web user binary.
|
|
</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>
|
|
Check all variables which are submitted.
|
|
</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
Here is an improved script:
|
|
<example>
|
|
<title>More secure file name checking</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
// removes a file from the hard drive that
|
|
// the PHP user has access to.
|
|
$username = $_SERVER['REMOTE_USER']; // using an authentication mechanism
|
|
$userfile = basename($_POST['user_submitted_filename']);
|
|
$homedir = "/home/$username";
|
|
|
|
$filepath = "$homedir/$userfile";
|
|
|
|
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, $logstring);
|
|
fclose($fp);
|
|
|
|
echo htmlentities($logstring, ENT_QUOTES);
|
|
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
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:
|
|
<example>
|
|
<title>More secure file name checking</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$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");
|
|
}
|
|
|
|
//etc...
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</para>
|
|
<para>
|
|
Depending on your operating system, there are a wide variety of files
|
|
which you should be concerned about, including device entries (/dev/
|
|
or COM1), configuration files (/etc/ files and the .ini files),
|
|
well known file storage areas (/home/, My Documents), etc. For this
|
|
reason, it's usually easier to create a policy where you forbid
|
|
everything except for what you explicitly allow.
|
|
</para>
|
|
<sect1 xml:id="security.filesystem.nullbytes">
|
|
<title>Null bytes related issues</title>
|
|
<simpara>
|
|
As <acronym>PHP</acronym> uses the underlying C functions for filesystem related
|
|
operations, it may handle null bytes in a quite unexpected way.
|
|
As null bytes denote the end of a string in C, strings containing them
|
|
won't be considered entirely but rather only until a null byte occurs.
|
|
|
|
The following example shows a vulnerable code that demonstrates this problem:
|
|
</simpara>
|
|
<example>
|
|
<title>Script vulnerable to null bytes</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$file = $_GET['file']; // "../../etc/passwd\0"
|
|
if (file_exists('/home/wwwrun/'.$file.'.php')) {
|
|
// file_exists will return true as the file /home/wwwrun/../../etc/passwd exists
|
|
include '/home/wwwrun/'.$file.'.php';
|
|
// the file /etc/passwd will be included
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
<para>
|
|
Therefore, any tainted string that is used in a filesystem operation should always
|
|
be validated properly. Here is a better version of the previous example:
|
|
</para>
|
|
<example>
|
|
<title>Correctly validating the input</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
$file = $_GET['file'];
|
|
|
|
// Whitelisting possible values
|
|
switch ($file) {
|
|
case 'main':
|
|
case 'foo':
|
|
case 'bar':
|
|
include '/home/wwwrun/include/'.$file.'.php';
|
|
break;
|
|
default:
|
|
include '/home/wwwrun/include/main.php';
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</sect1>
|
|
|
|
</chapter>
|
|
|
|
<!-- Keep this comment at the end of the file
|
|
Local variables:
|
|
mode: sgml
|
|
sgml-omittag:t
|
|
sgml-shorttag:t
|
|
sgml-minimize-attributes:nil
|
|
sgml-always-quote-attributes:t
|
|
sgml-indent-step:1
|
|
sgml-indent-data:t
|
|
indent-tabs-mode:nil
|
|
sgml-parent-document:nil
|
|
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
|
sgml-exposed-tags:nil
|
|
sgml-local-catalogs:nil
|
|
sgml-local-ecat-files:nil
|
|
End:
|
|
vim600: syn=xml fen fdm=syntax fdl=2 si
|
|
vim: et tw=78 syn=sgml
|
|
vi: ts=1 sw=1
|
|
-->
|