I've seen a lot of scripts and tutorials where people use the file
type (or mime type) to validate if a file is allowed to be uploaded. This
is inherently dangerous and will allow 'banned' files to get
around your filter. In this article I will demonstrate how this is done
and include some better ways to make these checks.
When writing web applications, or any for that fact, one thing to remember
is trust nothing. One can't stress this enough, especially when it
comes to user input. Users can, and will enter all sorts that will make
your application go coocoo. When I say user input, I don't mean just
anything directly from the user, I mean anything that comes from outside
the application.
Going back to our example, as you've probably now realised, file
uploads and the information they contain come from the user. This needs to
be checked. But I am checking you say! See my example above. Yes, but
you're checking user input against user input - do you know where that
mime type came from ? It all came from the user (or browser in this case).
Sure most browsers will set this information to be correct but Joe Hacker
won't.
Lets see what he'd use to set a mime type to pass the validation code
and upload a .php file;
As you can see, all he needs to do is set $content_type to something your
filter allows and he can upload any file he likes. In this case it was a
PHP file and it only echos out a small sentence. What if it does something
drastically more evil ?
Here's a sample script you can use to test the above against.
if(in_array($_FILES['file']['type'],$allowed))
{
echo 'The file is cool :)';
echo '<br>Filename:
<b>',$_FILES['file']['name'];
echo '</b><br>Filetype:
<b>',$_FILES['file']['type'];
echo '</b><br>File
contains;<p>',htmlspecialchars(file_get_contents($_FILES['f
ile']['tmp_name']),ENT_QUOTES);
}
else
{
echo 'The file is bad :(';
}
?>
Better ways to check the file.
When checking user input, it should only contain what you're
expecting. Does it really need numbers in it ? Does it really need
'funny' characters ? Figure out what the data should contain and
check for that. If it contains anything you're not expecting, error
out and tell the user to correct it. Regular expressions and the string
functions are great for this.
As long as your web server's mime handling is set up correctly, these
examples should be more than adequate;
if(strlen($filename)>32)
{
die('Filename length is too long');
}
// process upload
?>
This is just a check against the file's name/extension as that is
where the most immediate danger lies. Actual file validation is beyond the
scope of this article. For information on what any of any these PHP
functions do please see www.php.net/function_name. For information on file
uploads please see rfc1867.
posted by tress on Thursday 28th July 2005, 08:14:20
Good info, though at first read I thought my extension checking code was
insufficient, but I guess that's the right way, though I should
probably combine it with MIME checking too.
if this is graphics, maybe you should try and overwrite files headers in
parts where it is possible. this will make sure that whatever script it
was, it is broken and won't work after upload.