Hi all! I have found a way to limit the effects of Cross Site Scripting attacks - possibly stopping them completely. The idea is to write a 'closed site' that will not allow passing of variables on the URL line. This has been tested with apache 1.3.26, php4 etc under SuSE 8.1 pro on my box. (site is still under development - though I may put it on line for testing purposes if you want to contact me first) I've used special FORM POST buttons written for passing values between php4 scripts in a closed environment. (please see below) The only way into the website is via a URL WITHOUT anything passed after the ? of the URL. Once a user is on the homepage, they have to use the special FORM POST buttons to navigate this site. This is what I use on my website, which is STILL under construction, due to security issues, such as XSS. Any attempt to pass variables with the URL will be identified and the script will terminate! Would this be of use in stopping/limiting cross site scripting exploits, ref: CA-2000-02 ? Here it is: // this line needs to be called at the begining of each php4 // script, to put the URL query string into the global scope. // register_globals in php.ini must be set to OFF // or this method will not work! $url_string = $_SERVER["QUERY_STRING"]; /*-------------------------------------------------------------*/ /* Call this function (from an included file) at the start of each php4 script to detect and stop variables from being passed in the URL query string. */ function url_check($url_string) { if (DEBUG_CODE) { echo "common.url string: '$url_string' <BR>"; } if ('' <> $url_string) { echo '<P> Passing of variables by URL query string is not supported! <BR>'. 'Program terminating now - Please try again'; echo '<P> Found in URL -> ' . $url_string . '<BR>'; exit(); } } /*-------------------------------------------------------------*/ So, if an attacker tries to pass anything on the URL line, it will not be accepted! Then, instead of using an HREF link, I use a custom php4 function like the one below, for each script to be called. All these 'buttons' live in an include file, that is included at the start of each executable script. Other advantages are that the button is declared only once, but available to ALL pages that include it! This makes updating variables passed to other pages very easy to, as they ONLY need to be modified once, in the included file! As register_globals is OFF, the hidden variables passed from the calling script, have to be made available to the called script with: /*--------------------------------------------------*/ // get form button POST variables from array $host_name = $HTTP_POST_VARS["host_name"]; $debug_value = $HTTP_POST_VARS["debug_value"]; $table_output = $HTTP_POST_VARS["table_output"]; $advcd_search = $HTTP_POST_VARS["advcd_search"]; // $xyz_var_name = $HTTP_POST_VARS["xyz_var_name"]; /*--------------------------------------------------*/ At the start of each script, before they are accessed. This puts them back into the global scope, as if register_globals was set to ON. As you can see from below, this is just a simple HTML form, with no INPUT controls, so all the user sees is the submit button, with a descriptive name telling them what pressing the button will do. /*---------------------------------------------------------*/ /* (simple) Search for a mutual property exchange button */ /*---------------------------------------------------------*/ function simple_SEARCH_button($text) { // declare the following variables as global to access them global $debug_value; global $table_output; ?> <!-- back into HTML mode --> <FORM ACTION="./search.hml" METHOD="POST"> <P ALIGN=CENTER> <INPUT TYPE="SUBMIT" VALUE="<?php echo $text; ?>"> </P> <!-- ========================================================== --> <!-- pass the following hidden variables with the form --> <INPUT TYPE="HIDDEN" NAME="debug_value" VALUE="<?php echo $debug_value; ?>"> <INPUT TYPE="HIDDEN" NAME="table_output" VALUE="<?php echo $table_output; ?>"> <INPUT TYPE="HIDDEN" NAME="advcd_search" VALUE="OFF"> <!-- ========================================================== --> </FORM> <?php // back into php mode } // end of function simple_SEARCH_button($text) /*-------------------------------------------------------------*/ /*--------------------------------------------------*/ /* Advanced search for a property exchange button */ /*--------------------------------------------------*/ function ADVCD_SEARCH_button($text) { // declare the following variables as global to access them global $debug_value; global $table_output; ?> <!-- back into HTML mode --> <FORM ACTION="./search.hml" METHOD="POST"> <P ALIGN=CENTER> <INPUT TYPE="SUBMIT" VALUE="<?php echo $text; ?>"> </P> <!-- ========================================================== --> <!-- pass the following hidden variables with the form --> <INPUT TYPE="HIDDEN" NAME="debug_value" VALUE="<?php echo $debug_value; ?>"> <INPUT TYPE="HIDDEN" NAME="table_output" VALUE="<?php echo $table_output; ?>"> <INPUT TYPE="HIDDEN" NAME="advcd_search" VALUE="ON"> <!-- ========================================================== --> </FORM> <?php // back into php mode } // end of function ADVCD_SEARCH_button($text) /*-------------------------------------------------------------*/ Then, to call for example, the script search.hml from another page I use: <TR> <TD COLSPAN="100%"> <?php // back into php mode simple_SEARCH_button('Search for a mutual property exchange'); ?> <!-- back into HTML mode --> <TR> <TD COLSPAN="100%"> <?php // back into php mode ADVCD_SEARCH_button('Advanced search for a property exchange'); ?> <!-- back into HTML mode --> <TR> <TD COLSPAN="100%"> <?php // back into php mode ADD_REC_button('Add your exchange details to the database'); ?> <!-- back into HTML mode --> <TR> <TD COLSPAN="100%"> <?php // back into php mode EDIT_REC_button('Update your details in the database'); ?> <!-- back into HTML mode --> So, variables are passed between the php4 scripts, as HIDDEN HTML FORM variables, and not on the URL line. AFAIK, this makes altering values passed between scripts very difficult for attackers, which is what is intended. If you have any comments or questions please contact me, Kind Regards - Keith Roberts
Hello, Am Donnerstag, 04. Dezember 2003 23:38 schrieb Keith Roberts:
// register_globals in php.ini must be set to OFF // or this method will not work!
Sorry, this is wrong. If register_globals is set to ON, this will also work. It is just depending on your PHP version if $_SERVER is set. According to the PHP documentation, the $_* variables are always set since PHP 4.1. $HTTP_*_VARS are always set since PHP 4.0.3 (in older versions "track_vars" must be set).
Why don't you use $_GET? $HTTP_*_VARS is deprecated since the $_* variables were introduced. Also, $_GET is shorter and can be typed faster ;-)
Depending on where $text comes from, htmlentities($text) would be more safe.
Again: better use htmlentities()
So, variables are passed between the php4 scripts, as HIDDEN HTML FORM variables, and not on the URL line.
Hidden? No. Just have a look at the page source in your browser.
AFAIK, this makes altering values passed between scripts very difficult for attackers, which is what is intended.
Not really. If somebody wants to attack your site, he just has to create a little form with some "hidden" elements... OK, this can't be done in a link directly, but it isn't really safe. Just think about the following, which could be from an attacker: <INPUT TYPE="HIDDEN" NAME="debug_value" VALUE="evil string"> <INPUT TYPE="HIDDEN" NAME="table_output" VALUE="let's hack! <table><tr><td>hacked!</td></tr></table>"> <INPUT TYPE="HIDDEN" NAME="advcd_search" VALUE="OFF"> The only thing you'll notice in your logfile is a "strange" referrer. (BTW: If you check the referrer, make sure to allow requests without a referrer - some browsers don't send it and some proxies filter it out.) Another question: Did you ever think about using sessions? With sessions, you can keep your "hidden" variables server-side and only pass a session-id to the browser. Gruß Christian Boltz -- Fontlinge developer Fontlinge - font management for Linux / Schriftenverwaltung for Linux Infos and Download: http://www.gesindel.de
Hello, Am Donnerstag, 04. Dezember 2003 23:38 schrieb Keith Roberts:
// register_globals in php.ini must be set to OFF // or this method will not work!
Sorry, this is wrong. If register_globals is set to ON, this will also work. It is just depending on your PHP version if $_SERVER is set. According to the PHP documentation, the $_* variables are always set since PHP 4.1. $HTTP_*_VARS are always set since PHP 4.0.3 (in older versions "track_vars" must be set).
Why don't you use $_GET? $HTTP_*_VARS is deprecated since the $_* variables were introduced. Also, $_GET is shorter and can be typed faster ;-)
Depending on where $text comes from, htmlentities($text) would be more safe.
Again: better use htmlentities()
So, variables are passed between the php4 scripts, as HIDDEN HTML FORM variables, and not on the URL line.
Hidden? No. Just have a look at the page source in your browser.
AFAIK, this makes altering values passed between scripts very difficult for attackers, which is what is intended.
Not really. If somebody wants to attack your site, he just has to create a little form with some "hidden" elements... OK, this can't be done in a link directly, but it isn't really safe. Just think about the following, which could be from an attacker: <INPUT TYPE="HIDDEN" NAME="debug_value" VALUE="evil string"> <INPUT TYPE="HIDDEN" NAME="table_output" VALUE="let's hack! <table><tr><td>hacked!</td></tr></table>"> <INPUT TYPE="HIDDEN" NAME="advcd_search" VALUE="OFF"> The only thing you'll notice in your logfile is a "strange" referrer. (BTW: If you check the referrer, make sure to allow requests without a referrer - some browsers don't send it and some proxies filter it out.) Another question: Did you ever think about using sessions? With sessions, you can keep your "hidden" variables server-side and only pass a session-id to the browser. Gruß Christian Boltz -- Fontlinge developer Fontlinge - font management for Linux / Schriftenverwaltung for Linux Infos and Download: http://www.gesindel.de
participants (2)
-
Christian Boltz
-
Keith Roberts