Guide to PHP SecurityQuicksearchCalendar
|
Thursday, July 14. 2005Path Disclosure and PHP
In the past few days I've been testing a number of my own applications and scripts as well as various bits and pieces of applications written by others that I use, using an automated scanning tool I have written. One particular issue I came across, common to all applications is the inevitable "path disclosure vulnerability". The premise behind this so called vulnerability is that remote attackers by specifying certain value can make the script report it's own location on disk. Theoretically this combined with another vulnerability could be used to do *something* or rather then potentially could be bad. As you can probably tell, I don't see this as a something to be terribly concerned about in most cases.
To demonstrate consider the following, most PHP function that take strings as parameters, like our favorite validation functions such as htmlspecialchars(), addslashes(), and so on will raise warnings when values they are passed are arrays rather then strings. This means that to get the path of the script, you simply need to pass the arguments as arrays rather then expected strings and then simply read the warning message generated by PHP to see the error. PHP: To solve this problem there are just two solutions: 1) Disable displaying of errors to screen and instead log them to a file, so if the Warning or Notice regarding array being used as a string is emitted, it won't be shown to the user using the site. This can be done within the script through: PHP: 2) Meticulously go through the code forcing PHP to cast the data to the desired type, via (int), (float) or (string) casts, this too will eliminate the Notice or Warning message. Comments
Display comments as
(Linear | Threaded)
I never though on this.. thanks for the tips!
BTW, there's a tipo in the example: // script.php?name[]=foo instead of expected script.php?id=foo should be: // script.php?name[]=foo instead of expected script.php?name=foo
Why not simply initialize the Variables?
if (!isset($_GET['name'])) $_GET['name']='default';
This won't work since the parameter is not missing, isset() will return true, it's just that he type of the data is an array and not the expected string.
I have looked at the plain security that is in practice by most PHP developers and it is not very pretty. Like you said passing an array instead of a string forces this error.
I have always thought of type casting as something that should be done anyway, it is highly important not only to getting the right kind of data but also foring it to stay away from some unexpected types. Also to get rid of the notice error when trying to do the index it is just easier to just do the ternary operator $name = empty() ? null : htmlspecialchars((string) $_GET['name']); Thus keeping my expected variable set meanwhile checking to see that it exists and it is not empty or 0. For a production environment I do not actually have error reporting on except for the E_USER_* error types. This way my custom error handler handles user errors and I log all the rest. Thus keeping the errors I trigger showing and without a file or line numbers and hiding any other type of error that may have occurred.
Try it this way
if(is_array($_GET['name']) ){ foreach( $_GET['name'] as $get_name); $name = htmlspecialchars($get_name); //doe somthing with $name }else{ $name = htmlspecialchars($_GET['name']); } by the way, comment does not spport php tags
yeah .. a old problem .. is_string && is_array checks ... anyway its so simple to get all these errors:
look for var checks like isset, look to the docs, look for smth. like string type arguments and go post a flame on your projects mailinglist (as i do as a QAist) ... or just cast .... (you cant respond to error cases by just casting) The question is how can i make my coders aware of THINK before commit `? Anyway, if somebody would have read unit-test tutorials (not test first stuff), there is a simple way to test input of functions of your api: example integer input - correct value - min and max - min-1, max+1 - wrong type: string - wrong type object We have a bavarian saying: Was der Bauer ned kennt des Frisst er ned means What the farmer doent know hes not eating anyway .. if youre throught with these, fix all the stuff occuring .. youll be safe from that automatically. Remark: The missing strong typing makes that kind of test important to php apps. YOu cannt enter a string value in a java method interface thats defined for another type
type casting doesn't help, if you have something like this
script.php?name[]=foo;name[]=foo2;name[]=foo3
youre right ... that helps to ensure that strings dont go into numeric mysql fields .. btw a problem in the same field ...
The main question is: how to maintain the non existant typing From my point of view, there is an answer in the upcomin wsdl_gen by pear
If you take something like this:
$name = array('foo', 'foo2', 'foo3'); $name = empty($name) ? null : htmlspecialchars((string) $name); echo $name; you will get the string Array. Why would this nessesarily be a bad thing, this normally this is after you have your predetermined business logic but really if they passed that it should be of the right constrant at that time anyhow. Would you want one of the names to be used? I think that if the input is wrong in the first place we don't want it so checking types is normally a good option. Something first of all like: if (!empty($_GET['name']) && is_string($_GET['name'])) { $name = htmlspecialchars((string) $_GET['name']); } because see if it is an array you shouldn't really be parsing it anyway... Input parameters should be very strict to be of the type that you want instead of trying to defaultly handle it if someone is fooling around with the application. A good security measure if people are fooling around is to log it, force them to go back and if there is a continuing of 2 or 3+ tries block the requests and block there address from future usage of the site. Now to see what I mean for the input run a sript like the following: /** input parameters *name[]=foo&name[]=foo2&name[]=foo3 * expected result: name not set. */ $name = 'name not set'; if (!empty($_GET['name']) && is_string($_GET['name'])) { $name = htmlspecialchars((string) $_GET['name']); } echo $name; this is where type casting really helps and makes things much nicer... better to be extremely paranoid than to let a varable seep in. Now really you wouldn't need to type cast there telling it to cast it to a string because the is_string already checks for that constraint... why do I do it anyway? If there ends up being a bug in php that allows a constraint to change in the way it handles there is always a backup. Pretty much known as double handling and expecting problems to be where there may never be problems. Mike
Mike, I tried your code and at first it doesn't work as expected. If I use the code without the $name hardcoded, and access the page like:
page.php?name[]=foo And then in the page I do: if (!empty($_GET['name']) && is_string($_GET['name'])) { $name = htmlspecialchars((string) $_GET['name']); } echo $name; The echoed result is: array I looked a bit better and the problem is with the testenvironment in which I was testing, register globals was on. And therefore, $name never got "filtered". If I change the code to: if (!empty($_GET['name']) && is_string($_GET['name'])) { $cleanname = htmlspecialchars((string) $_GET['name']); } echo $cleanname; The filtering takes place (nothing is echoed). If someone has some more to say about this, please do. Quite scary stuff, this is. |
ArchivesCategoriesSyndicate This BlogBlog Administration |
|||||||||||||||||||||||||||||||||||||||||||||||||










Comments