From 15aecc4c4f2107262ee91ae3270d4adbfefbd9ee Mon Sep 17 00:00:00 2001 From: Ron Chmara Date: Sat, 28 Jul 2001 21:57:49 +0000 Subject: [PATCH] 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 --- chapters/security.xml | 191 +++++++++++++++++++++++++++++++++++------- security/index.xml | 191 +++++++++++++++++++++++++++++++++++------- 2 files changed, 326 insertions(+), 56 deletions(-) diff --git a/chapters/security.xml b/chapters/security.xml index 8acdbf7d59..51cd85e7ce 100644 --- a/chapters/security.xml +++ b/chapters/security.xml @@ -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. 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. 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. 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. + + There are some simpler solutions. By using + open_basedir() 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. + @@ -369,7 +377,7 @@ echo "/home/../etc/passwd has been deleted!"; <?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!"; ?> - 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: More secure file name checking <?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... ?> + + 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)) Error Reporting - + + With PHP security, there are two sides to error reporting. One is + beneficial to increasing security, the other is detrimental. + + 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. - - + 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: + + Attacking Variables with a custom HTML page + +<form method="post" action="attacktarget?username=badfoo&password=badfoo"> +<input type="hidden" name="username" value="badfoo"> +<input type="hidden" name="password" value="badfoo"> +</form> + + + + 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)) highlight_string, or highlight_file as a debugging measure, but in a live site, this can expose hidden variables, unchecked syntax, - and other dangerous information. - - - 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: + + Exploiting common debugging variables + +<form method="post" action="attacktarget?errors=Y&showerrors=1"&debug=1"> +<input type="hidden" name="errors" value="Y"> +<input type="hidden" name="showerrors" value="1"> +<input type="hidden" name="debug" value="1"> +</form> + + + + + Regardless of the method of error handling, the ability to probe a + system for errors leads to providing an attacker with more + information. + + + 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. - - + + 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. - - + + 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. - - + + 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. - + + + One way of catching this issue ahead of time is to make use of + PHP's own error_reporting(), 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. + + Finding dangerous variables with E_ALL + +<?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"); +} +?> + + + + + + Using Register Globals + + 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. + + + 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. + + Working without register_globals=off + +<?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"); +} +?> + + + + Working with register_globals = off + +<?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"); +} +?> + + + 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. + + Detecting variable poisoning + +<?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; +} +?> + + + + + User Submitted Data @@ -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). - + General considerations diff --git a/security/index.xml b/security/index.xml index 8acdbf7d59..51cd85e7ce 100644 --- a/security/index.xml +++ b/security/index.xml @@ -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. 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. 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. 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. + + There are some simpler solutions. By using + open_basedir() 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. + @@ -369,7 +377,7 @@ echo "/home/../etc/passwd has been deleted!"; <?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!"; ?> - 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: More secure file name checking <?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... ?> + + 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)) Error Reporting - + + With PHP security, there are two sides to error reporting. One is + beneficial to increasing security, the other is detrimental. + + 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. - - + 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: + + Attacking Variables with a custom HTML page + +<form method="post" action="attacktarget?username=badfoo&password=badfoo"> +<input type="hidden" name="username" value="badfoo"> +<input type="hidden" name="password" value="badfoo"> +</form> + + + + 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)) highlight_string, or highlight_file as a debugging measure, but in a live site, this can expose hidden variables, unchecked syntax, - and other dangerous information. - - - 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: + + Exploiting common debugging variables + +<form method="post" action="attacktarget?errors=Y&showerrors=1"&debug=1"> +<input type="hidden" name="errors" value="Y"> +<input type="hidden" name="showerrors" value="1"> +<input type="hidden" name="debug" value="1"> +</form> + + + + + Regardless of the method of error handling, the ability to probe a + system for errors leads to providing an attacker with more + information. + + + 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. - - + + 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. - - + + 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. - - + + 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. - + + + One way of catching this issue ahead of time is to make use of + PHP's own error_reporting(), 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. + + Finding dangerous variables with E_ALL + +<?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"); +} +?> + + + + + + Using Register Globals + + 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. + + + 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. + + Working without register_globals=off + +<?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"); +} +?> + + + + Working with register_globals = off + +<?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"); +} +?> + + + 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. + + Detecting variable poisoning + +<?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; +} +?> + + + + + User Submitted Data @@ -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). - + General considerations