Really, I just wanted an excuse to complain about Apache's *.php.* thing. =\
Does anyone want me to continue it?
> Subtle security problems
>>> Apache .php.* problem
If you think stopping a file from ending in .php is enough to stop it
from being executed, think again. (assuming this would also allow
other extensions such as .shtml, .pl etc. if they were enabled, but
they're ignored for now)
Code: Select all
/* Bad! */
if (substr($filename,-4) == '.php')
{
exit("No scripts please.");
}
A file called test.php.txt containing php code won't be executed
A file called test.php.jpg containing php code won't be executed
A file called test.php.fsdf containing php code will be executed
(unless you have set .fsdf as a known type =)
If a filename contains .php followed by a file extension unknown to
Apache (no mime type associated) - It will be executed if accessable
from a browser.
I'd call this a bug, but I'm sure it would have been picked up by the
Apache devs already if it was.
This problem affects the PHP module on both Linux and Windows Apache
servers.
The way I handle the problem is to block .*\.php.*$ on my server, but
this may be sufficient:
Code: Select all
/* "Okay"? */
if (strpos($filename,'.php') !== false) /* Block .php anywhere in filename */
{
exit("No scripts please.");
}
>>> Poison Null (%00)
Once again, vunlerable on some webservers (*cough*Apache), but not others.
This is more well known than the above bug, but is just as dangerous.
Code: Select all
/* Seems harmless enough? Right? Wrong. */
/* Haha, and this is vulnerable to
the above problem too */
/* Hell it's exactly the same example really... */
if (substr($_GET['filename'],-4) == '.php')
{
exit("No scripts please.");
}
else
{
file_put_contents($_GET['filename'],$some_user_inputted_data);
}
null characters, but when passed through file_put_contents, and eventually
reaches the libc function "fopen" - the string has to be truncated
at where the null is.
Examples:
?filename=file.txt -- allowed
?filename=file.php -- blocked by script
?filename=file.php%00.txt -- allowed by script
The simplest solution is to simply truncate it before you check it
Code: Select all
/* Yeah this still still vulnerable to *.php.* on
Apache, but it's just an example */
/* Terminate at null */
$filename = substr($_GET['filename'],0,(int)strpos($_GET['filename'],chr(0));
if (substr($filename,-4) == '.php')
{
exit("No scripts please.");
}
else
{
file_put_contents($filename,$some_user_inputted_data);
}
>>> Developing with magic_quotes enabled
If the config option magic_quotes_gpc is enabled, every value in $_GET,
$_POST and $_COOKIE will automatically be "escaped" (http://php.net/addslashes)
The other magic_quotes_* flags will also cause problems.
If your development machine has them enabled, and your production server
doesn't - It's not hard to accidently open up an SQL injection exploit.
Code: Select all
/* Safe on a server with magic_quotes_gpc */
/* Not safe at all otherwise */
mysql_query("SELECT * FROM users WHERE username = '{$_GET['username']}' AND password = '{$_GET['password']}'");
situation is reversed:
Code: Select all
/* Escaping user input like a good boy */
$author = mysql_real_escape_string($_GET['author']);
$message = mysql_real_escape_string($_GET['message']);
mysql_query("INSERT INTO posts (author,message) VALUES ('$author','$message')");
> Easy fixes to common problems
>>> Restricting file access to one directory
Code: Select all
/* Bad! */
if (is_file('files/'.$_GET['filename']))
readfile('files/'.$_GET['filename']);
The solution is simple, wrap the filename in basename()
Code: Select all
/* Better. */
if (is_file('files/'.basename($_GET['filename'])))
readfile('files/'.basename($_GET['filename']));
access, but that won't be covered right now.