Enhancing, per recent discussions on PHP-DEV, and adding section on using register_globals to increase security.

git-svn-id: https://svn.php.net/repository/phpdoc/en/trunk@52436 c90b9560-bf6c-de11-be94-00142212c4b1
This commit is contained in:
Ron Chmara 2001-07-28 21:57:49 +00:00
parent 18b317ad40
commit 15aecc4c4f
2 changed files with 326 additions and 56 deletions

View file

@ -265,29 +265,37 @@ AddHandler php-script .php
"nobody" user. This means a malicious script could access and modify
the database, even without a username and password. It's entirely
possible that a web spider could stumble across a database
adminisitror's web page, and drop all of your databases. You can
administrator's web page, and drop all of your databases. You can
protect against this with Apache authorization, or you can design
your own access model using LDAP, .htaccess files, etc. and include
that code as part of your PHP scripts.
</simpara>
<simpara>
Often, once security is established to the point where the PHP user
(in this case, the apache user) has very little risk, it is
discovered that PHP now has been prevented from writing virus files
(in this case, the apache user) has very little risk attached to it,
it is discovered that PHP is now prevented from writing any files
to user directories. Or perhaps it has been prevented from accessing
or changing a non-public database. It has equally been secured from
writing files that it should, or entering database transactions.
or changing databases. It has equally been secured from writing
good and bad files, or entering good and bad database transactions.
</simpara>
<simpara>
A frequent security mistake made at this point is to allow apache
root permissions.
root permissions, or to escalate apache's abilitites in some other
way.
</simpara>
<simpara>
Escalating the Apache user's permissions to root is extremely
dangerous and may compromise the entire system, so sudo'ing,
chroot'ing ,or otherwise running as root should not be considered by
chroot'ing, or otherwise running as root should not be considered by
those who are not security professionals.
</simpara>
<simpara>
There are some simpler solutions. By using
<function>open_basedir()</function> you can control and restrict what
directories are allowed to be used for PHP. You can also set up
apache-only areas, to restrict all web based activity to non-user,
or non-system, files.
</simpara>
</sect1>
<sect1 id="security.filesystem">
@ -369,7 +377,7 @@ echo "/home/../etc/passwd has been deleted!";
&lt;?php
// removes a file from the hard drive that
// the PHP user has access to.
$username = $HTTP_REMOTE_USER; // use an authentication mechanisim
$username = $HTTP_REMOTE_USER; // using an authentication mechanisim
$homedir = "/home/$username";
@ -385,21 +393,28 @@ echo "$file_to_delete has been deleted!";
?&gt;
</programlisting>
</example>
Alternately, you may prefer to write a more customized check:
However, even this is not without it's 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">
&lt;?php
$username = getenv("REMOTE_USER");
$username = $HTTP_REMOTE_USER; // using an authentication mechanisim
$homedir = "/home/$username";
if (!ereg('^[^./][^/]*$', $userfile))
die('bad filename'); //die, do not process
die('bad filename'); //die, do not process
if (!ereg('^[^./][^/]*$', $username))
die('bad username'); //die, do not process
//etc...
?&gt;
</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),
@ -411,13 +426,29 @@ if (!ereg('^[^./][^/]*$', $userfile))
<sect1 id="security.errors">
<title>Error Reporting</title>
<simpara>
<para>
With PHP security, there are two sides to error reporting. One is
beneficial to increasing security, the other is detrimental.
</para>
<para>
A standard attack tactic involves profiling a system by feeding
it improper data, and checking for the kinds, and contexts, of the
errors which are returned. This allows the system cracker to probe
for information about the server, to determine possible weaknesses.
</simpara>
<simpara>
For example, if an attacker had gleaned information about a page
based on a prior form submission, they may attempt to override
variables, or modify them:
<example>
<title>Attacking Variables with a custom HTML page</title>
<programlisting role="php">
&lt;form method="post" action="attacktarget?username=badfoo&amp;password=badfoo"&gt;
&lt;input type="hidden" name="username" value="badfoo"&gt;
&lt;input type="hidden" name="password" value="badfoo"&gt;
&lt;/form&gt;
</programlisting>
</example>
</para>
<para>
The PHP errors which are normally returned can be quite helpful to a
developer who is trying to debug a script, indicating such things
as the function or file that failed, the PHP file it failed in,
@ -427,16 +458,35 @@ if (!ereg('^[^./][^/]*$', $userfile))
<function>highlight_string</function>, or
<function>highlight_file</function> as a debugging measure, but in
a live site, this can expose hidden variables, unchecked syntax,
and other dangerous information.
</simpara>
<simpara>
For example, the very style of a generic error indicates a system
and other dangerous information. Especially dangerous is running
code from known sources with built-in debugging handlers, or using
common debugging techniques. If the attacker can determine what
general technique you are using, they may try to brute-force a page,
by sending various common debugging strings:
<example>
<title>Exploiting common debugging variables</title>
<programlisting role="php">
&lt;form method="post" action="attacktarget?errors=Y&amp;showerrors=1"&amp;debug=1"&gt;
&lt;input type="hidden" name="errors" value="Y"&gt;
&lt;input type="hidden" name="showerrors" value="1"&gt;
&lt;input type="hidden" name="debug" value="1"&gt;
&lt;/form&gt;
</programlisting>
</example>
</para>
<para>
Regardless of the method of error handling, the ability to probe a
system for errors leads to providing an attacker with more
information.
</para>
<para>
For example, the very style of a generic PHP error indicates a system
is running PHP. If the attacker was looking at an .html page, and
wanted to probe for the back-end (to look for known weaknesses in
the system), by feeding it the wrong data they may be able to
determine that a system was built with PHP.
</simpara>
<simpara>
</para>
<para>
A function error can indicate whether a system may be running a
specific database engine, or give clues as to how a web page or
programmed or designed. This allows for deeper investigation into
@ -445,15 +495,15 @@ if (!ereg('^[^./][^/]*$', $userfile))
an attacker can determine the order of authentication in a script,
(from the line number errors) as well as probe for exploits that
may be exploited in different locations in the script.
</simpara>
<simpara>
</para>
<para>
A filesystem or general PHP error can indicate what permissions
the webserver has, as well as the structure and organization of
files on the web server. Developer written error code can aggravate
this problem, leading to easy exploitation of formerly "hidden"
information.
</simpara>
<simpara>
</para>
<para>
There are three major solutions to this issue. The first is to
scrutinize all functions, and attempt to compensate for the bulk
of the errors. The second is to disable error reporting entirely
@ -461,8 +511,93 @@ if (!ereg('^[^./][^/]*$', $userfile))
handling functions to create your own error handler. Depending
on your security policy, you may find all three to be applicable
to your situation.
</simpara>
</para>
<para>
One way of catching this issue ahead of time is to make use of
PHP's own <function>error_reporting()</function>, to help you
secure your code and find variable usage that may be dangerous.
By testing your code, prior to deployment, with E_ALL, you can
quickly find areas where your variables may be open to poisoning
or modification in other ways. Once you are ready for deployment,
by using E_NONE, you insulate your code from probing.
<example>
<title>Finding dangerous variables with E_ALL</title>
<programlisting role="php">
&lt;?php
if ($username) { // Not initialized or checked before usage
$good_login = 1;
}
if ($good_login == 1) { // If above test fails, not initialized or checked before usage
fpassthru ("/highly/sensitive/data/index.html");
}
?&gt;
</programlisting>
</example>
</para>
</sect1>
<sect1 id="security.registerglobals">
<title>Using Register Globals</title>
<para>
One feature of PHP that can be used to enhance security is
configuring PHP with register_globals = off. By turning off
the ability for any user-submitted variable to be injected
into PHP code, you can restrict the amount of variable
poisoning a potential attacker may inflict.
</para>
<para>
While it does slightly increase the amount of effort required
to work with PHP, it has been argued that the benefits far
outweigh the effort.
<example>
<title>Working without register_globals=off</title>
<programlisting role="php">
&lt;?php
if ($username) { // can be forged by a user in get/post/cookies
$good_login = 1;
}
if ($good_login == 1) { // can be forged by a user in get/post/cookies,
fpassthru ("/highly/sensitive/data/index.html");
}
?&gt;
</programlisting>
</example>
<example>
<title>Working with register_globals = off</title>
<programlisting role="php">
&lt;?php
if($HTTP_COOKIE_VARS["username"]){ // can only come from a cookie
$good_login = 1; // cannot be forged by a user
fpassthru ("/highly/sensitive/data/index.html");
}
?&gt;
</programlisting>
</example>
By using this wisely, it's even possible to take preventative
measures to warn when forging is being attempted. If you know
ahead of time exactly where a variable should be coming from,
you can check to see if submitted data is inappropriate.
<example>
<title>Detecting variable poisoning</title>
<programlisting role="php">
&lt;?php
if ($HTTP_COOKIE_VARS["username"] &&
!$HTTP_POST_VARS["username"] &&
!$HTTP_GET_VARS["username"] ) {
$good_login = 1;
fpassthru ("/highly/sensitive/data/index.html");
} else {
mail("admin@example.com", "Possible breakin attempt", "$REMOTE_IP_ADDR");
echo "Security violation, admin has been alerted.";
exit;
}
?&gt;
</programlisting>
</example>
</para>
</sect1>
<sect1 id="security.variables">
<title>User Submitted Data</title>
@ -480,7 +615,7 @@ if (!ereg('^[^./][^/]*$', $userfile))
// somebody else's?
unlink ($evil_var);
// Write logging of their access... or maybe not?
// Write logging of their access... or maybe an /etc/password entry?
fputs ($fp, $evil_var);
// Execute something trivial.. or rm -rf *?
@ -537,7 +672,7 @@ exec ($evil_var);
operated upon).
</para>
</sect1>
<sect1 id="security.general">
<title>General considerations</title>
<simpara>

View file

@ -265,29 +265,37 @@ AddHandler php-script .php
"nobody" user. This means a malicious script could access and modify
the database, even without a username and password. It's entirely
possible that a web spider could stumble across a database
adminisitror's web page, and drop all of your databases. You can
administrator's web page, and drop all of your databases. You can
protect against this with Apache authorization, or you can design
your own access model using LDAP, .htaccess files, etc. and include
that code as part of your PHP scripts.
</simpara>
<simpara>
Often, once security is established to the point where the PHP user
(in this case, the apache user) has very little risk, it is
discovered that PHP now has been prevented from writing virus files
(in this case, the apache user) has very little risk attached to it,
it is discovered that PHP is now prevented from writing any files
to user directories. Or perhaps it has been prevented from accessing
or changing a non-public database. It has equally been secured from
writing files that it should, or entering database transactions.
or changing databases. It has equally been secured from writing
good and bad files, or entering good and bad database transactions.
</simpara>
<simpara>
A frequent security mistake made at this point is to allow apache
root permissions.
root permissions, or to escalate apache's abilitites in some other
way.
</simpara>
<simpara>
Escalating the Apache user's permissions to root is extremely
dangerous and may compromise the entire system, so sudo'ing,
chroot'ing ,or otherwise running as root should not be considered by
chroot'ing, or otherwise running as root should not be considered by
those who are not security professionals.
</simpara>
<simpara>
There are some simpler solutions. By using
<function>open_basedir()</function> you can control and restrict what
directories are allowed to be used for PHP. You can also set up
apache-only areas, to restrict all web based activity to non-user,
or non-system, files.
</simpara>
</sect1>
<sect1 id="security.filesystem">
@ -369,7 +377,7 @@ echo "/home/../etc/passwd has been deleted!";
&lt;?php
// removes a file from the hard drive that
// the PHP user has access to.
$username = $HTTP_REMOTE_USER; // use an authentication mechanisim
$username = $HTTP_REMOTE_USER; // using an authentication mechanisim
$homedir = "/home/$username";
@ -385,21 +393,28 @@ echo "$file_to_delete has been deleted!";
?&gt;
</programlisting>
</example>
Alternately, you may prefer to write a more customized check:
However, even this is not without it's 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">
&lt;?php
$username = getenv("REMOTE_USER");
$username = $HTTP_REMOTE_USER; // using an authentication mechanisim
$homedir = "/home/$username";
if (!ereg('^[^./][^/]*$', $userfile))
die('bad filename'); //die, do not process
die('bad filename'); //die, do not process
if (!ereg('^[^./][^/]*$', $username))
die('bad username'); //die, do not process
//etc...
?&gt;
</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),
@ -411,13 +426,29 @@ if (!ereg('^[^./][^/]*$', $userfile))
<sect1 id="security.errors">
<title>Error Reporting</title>
<simpara>
<para>
With PHP security, there are two sides to error reporting. One is
beneficial to increasing security, the other is detrimental.
</para>
<para>
A standard attack tactic involves profiling a system by feeding
it improper data, and checking for the kinds, and contexts, of the
errors which are returned. This allows the system cracker to probe
for information about the server, to determine possible weaknesses.
</simpara>
<simpara>
For example, if an attacker had gleaned information about a page
based on a prior form submission, they may attempt to override
variables, or modify them:
<example>
<title>Attacking Variables with a custom HTML page</title>
<programlisting role="php">
&lt;form method="post" action="attacktarget?username=badfoo&amp;password=badfoo"&gt;
&lt;input type="hidden" name="username" value="badfoo"&gt;
&lt;input type="hidden" name="password" value="badfoo"&gt;
&lt;/form&gt;
</programlisting>
</example>
</para>
<para>
The PHP errors which are normally returned can be quite helpful to a
developer who is trying to debug a script, indicating such things
as the function or file that failed, the PHP file it failed in,
@ -427,16 +458,35 @@ if (!ereg('^[^./][^/]*$', $userfile))
<function>highlight_string</function>, or
<function>highlight_file</function> as a debugging measure, but in
a live site, this can expose hidden variables, unchecked syntax,
and other dangerous information.
</simpara>
<simpara>
For example, the very style of a generic error indicates a system
and other dangerous information. Especially dangerous is running
code from known sources with built-in debugging handlers, or using
common debugging techniques. If the attacker can determine what
general technique you are using, they may try to brute-force a page,
by sending various common debugging strings:
<example>
<title>Exploiting common debugging variables</title>
<programlisting role="php">
&lt;form method="post" action="attacktarget?errors=Y&amp;showerrors=1"&amp;debug=1"&gt;
&lt;input type="hidden" name="errors" value="Y"&gt;
&lt;input type="hidden" name="showerrors" value="1"&gt;
&lt;input type="hidden" name="debug" value="1"&gt;
&lt;/form&gt;
</programlisting>
</example>
</para>
<para>
Regardless of the method of error handling, the ability to probe a
system for errors leads to providing an attacker with more
information.
</para>
<para>
For example, the very style of a generic PHP error indicates a system
is running PHP. If the attacker was looking at an .html page, and
wanted to probe for the back-end (to look for known weaknesses in
the system), by feeding it the wrong data they may be able to
determine that a system was built with PHP.
</simpara>
<simpara>
</para>
<para>
A function error can indicate whether a system may be running a
specific database engine, or give clues as to how a web page or
programmed or designed. This allows for deeper investigation into
@ -445,15 +495,15 @@ if (!ereg('^[^./][^/]*$', $userfile))
an attacker can determine the order of authentication in a script,
(from the line number errors) as well as probe for exploits that
may be exploited in different locations in the script.
</simpara>
<simpara>
</para>
<para>
A filesystem or general PHP error can indicate what permissions
the webserver has, as well as the structure and organization of
files on the web server. Developer written error code can aggravate
this problem, leading to easy exploitation of formerly "hidden"
information.
</simpara>
<simpara>
</para>
<para>
There are three major solutions to this issue. The first is to
scrutinize all functions, and attempt to compensate for the bulk
of the errors. The second is to disable error reporting entirely
@ -461,8 +511,93 @@ if (!ereg('^[^./][^/]*$', $userfile))
handling functions to create your own error handler. Depending
on your security policy, you may find all three to be applicable
to your situation.
</simpara>
</para>
<para>
One way of catching this issue ahead of time is to make use of
PHP's own <function>error_reporting()</function>, to help you
secure your code and find variable usage that may be dangerous.
By testing your code, prior to deployment, with E_ALL, you can
quickly find areas where your variables may be open to poisoning
or modification in other ways. Once you are ready for deployment,
by using E_NONE, you insulate your code from probing.
<example>
<title>Finding dangerous variables with E_ALL</title>
<programlisting role="php">
&lt;?php
if ($username) { // Not initialized or checked before usage
$good_login = 1;
}
if ($good_login == 1) { // If above test fails, not initialized or checked before usage
fpassthru ("/highly/sensitive/data/index.html");
}
?&gt;
</programlisting>
</example>
</para>
</sect1>
<sect1 id="security.registerglobals">
<title>Using Register Globals</title>
<para>
One feature of PHP that can be used to enhance security is
configuring PHP with register_globals = off. By turning off
the ability for any user-submitted variable to be injected
into PHP code, you can restrict the amount of variable
poisoning a potential attacker may inflict.
</para>
<para>
While it does slightly increase the amount of effort required
to work with PHP, it has been argued that the benefits far
outweigh the effort.
<example>
<title>Working without register_globals=off</title>
<programlisting role="php">
&lt;?php
if ($username) { // can be forged by a user in get/post/cookies
$good_login = 1;
}
if ($good_login == 1) { // can be forged by a user in get/post/cookies,
fpassthru ("/highly/sensitive/data/index.html");
}
?&gt;
</programlisting>
</example>
<example>
<title>Working with register_globals = off</title>
<programlisting role="php">
&lt;?php
if($HTTP_COOKIE_VARS["username"]){ // can only come from a cookie
$good_login = 1; // cannot be forged by a user
fpassthru ("/highly/sensitive/data/index.html");
}
?&gt;
</programlisting>
</example>
By using this wisely, it's even possible to take preventative
measures to warn when forging is being attempted. If you know
ahead of time exactly where a variable should be coming from,
you can check to see if submitted data is inappropriate.
<example>
<title>Detecting variable poisoning</title>
<programlisting role="php">
&lt;?php
if ($HTTP_COOKIE_VARS["username"] &&
!$HTTP_POST_VARS["username"] &&
!$HTTP_GET_VARS["username"] ) {
$good_login = 1;
fpassthru ("/highly/sensitive/data/index.html");
} else {
mail("admin@example.com", "Possible breakin attempt", "$REMOTE_IP_ADDR");
echo "Security violation, admin has been alerted.";
exit;
}
?&gt;
</programlisting>
</example>
</para>
</sect1>
<sect1 id="security.variables">
<title>User Submitted Data</title>
@ -480,7 +615,7 @@ if (!ereg('^[^./][^/]*$', $userfile))
// somebody else's?
unlink ($evil_var);
// Write logging of their access... or maybe not?
// Write logging of their access... or maybe an /etc/password entry?
fputs ($fp, $evil_var);
// Execute something trivial.. or rm -rf *?
@ -537,7 +672,7 @@ exec ($evil_var);
operated upon).
</para>
</sect1>
<sect1 id="security.general">
<title>General considerations</title>
<simpara>