In my uploading user's avatar scenario, I change users picture extension to jpg. Is this making the browsers to act differently? Is it prone to error for browsers when they read users avatar? Moreover, does it make my web app more secure when an attacker upload a php file?
When you are uploading files you should insure that the file extension is one that you approve, other wise known as a white list approach. You shouldn't rename every extension to .jpeg, this will cause problems. Most HTTPD's will set the mime type based on the file extension, which informs the browser of how to decode the content.
There is another problem with file uploads. Apache will "fall back" on the 2nd file extension if it doesn't have a mime type for the first file extension. So by default,
backdoor.php.fjfl will execute as a .php file, Ouch. I recommend renaming the file, like to the primary key, on top of having a white-list of file extensions.
Even if the user is uploading a valid image it can still cause problems for security. For instance, the images metadata could contain a php tag, which is useful in turning a simple Local File Include vulnerability into remote code execution.
As @Rook said you shouldn't change the extension as it will cause problems. He also pointed out that the metadata could contain malicious data, which is very true. So here are the steps you should follow, roughly:
- Check file size - Is it massive enough to break the web server through DoS?
- Check extension - Is it a known extension that you support, e.g. jpg, png, gif
- If extension is good, check file header - Even though it says jpg, it could contain GIF/PNG/EXE header, etc
- Strip metadata from file - Don't even read it, just delete it
- Convert to format of your choice if you want it in a given format
In the event that any of the above checks fail you just stop the process and delete the file and return a simple error message explaining that the image is no good.
(1) Never use any part of a user-submitted filename in your stored filename. Sanitising a filename is a treacherous job, not as easy to do reliably as it looks at all—especially if your app might end up running on a Windows as well as Linux server.
Store it as something like
2395862.jpeg, with the filename derived from eg the primary key of the related database entity.
If you need to present a ‘friendly’ name to the end browser, use a rewrite so that you can serve it as
/avatar/2395862/anyname.jpeg whilst it's actually stored as
(2) Ensure you store the files in a separate directory that contains nothing else. This directory should be the only one the web script user has write access to. It should have locked-down permissions so only static files can be served from it. On Apache this should be done from a higher-level
httpd.conf and overrides turned off so that it's not possible for writing a
.htaccess file in this directory to have any effect.
For this reason, you should consider any user-uploaded-file repository to be compromised for cross-site-scripting, and mitigate this by serving all your user-uploaded files from a different domain. That way any injected script won't be able to cross-site-script into your main web site. Ensure that there is no way to access to user-uploaded resources from your main web site.
The separate site for user resources can be a completely different domain, or it can be a subdomain. But if it's a subdomain you must ensure that there is nothing on the parent domain: so if you serve user resources from
avatars.myforum.com your main forum can be at
www.myforum.com but it must not be at
myforum.com. Otherwise browsers will share cookies between them.