I know there is a big amount of questions about this but I cannot get one that involves all I want to be aware of.
What I want to do is to allow the users of my webpage to upload images with a form. And I want to do this process secure, or at least as much secure I can.
I do not know too much about security in terms of deep inside of it, but I am aware of about all the consequences that a insecure webpage can produce. And I cannot be quiet thinking that my webpage is insecure or that anyone is not going to enter into my webpage because it does not have enough visits(I am realist).
At this point, I know that all the checks about security have to be done on server side instead of client side (or in both).
I know that a file can be fooled as an image and run malicious code so I searched about methods to avoid this. This is what I could find to check before store the image on the server:
From $_FILES
:
$_FILES['file']['name']
: To check that the file that I have uploaded have a name. To know that the file exists.$_FILES['file']['error']
: To check if the image have an error.$_FILES['file']['size']
: To check that the size of the image is bigger than 0.$_FILES['file']['type']
: To check that the type of the file is an image but it is not recommended because PHP
does not check it.General functions:
Check magic numbers
to verify the image type.
exif_imagetype()
: To check the type of an image.
getimagesize()
: To check if it returns a 0 which means that the file is not an image.
imagecreatefromstring()
: To create a new image giving a string. If it cannot be created, then is not an image.
imagepng
: To create a PNG
image to remove all meta-data (using imagecreatetruecolor()
and imagecopy()
).
But the problem I have is that I do not know if I should use all of these methods or just avoid or add some of them (because some of them seems redundant).
And my questions are:
Note: I am not searching about personal opinion. I tried to gather all info I could, but I cannot be sure if it is ok or not talking about security terms. If you can put examples of something that it is forgotten it would be great.
Thanks in advance!
PHP is not limited to creating just HTML output. It can also be used to create and manipulate image files in a variety of different image formats, including GIF , PNG , JPEG , WBMP , and XPM .
To answer your questions:
Depending on your application, the logic for any secure upload should flow something like this:
Is the user logged in? (optional)
// make sure user is logged in
if (!$user->loggedIn()) {
// redirect
}
Does the user have permission? (optional)
// make sure user has permission
if (!$user->isAllowed()) {
// redirect
}
Was the form submitted?
// make sure form was submitted
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
Is the form input valid?
// validate CSRF token
// ...
// make sure there were no form errors
if ($_FILES['file']['error'] == UPLOAD_ERR_OK) {
// make sure the file size is good
if ($_FILES['file']['size'] <= MAX_FILE_UPLOAD) {
// make sure we have a valid image type
$type = exif_imagetype($_FILES['file']['tmp_name']);
if ($type !== false) {
// make sure we check the type against a whitelist
if (in_array(ltrim(image_type_to_extension($type), '.'), array('jpeg', 'jpg', 'png'))) {
Even after validating, never trust user input
// give the file a unique name
$hash = hash_file('sha1', $_FILES['file']['tmp_name']);
$ext = image_type_to_extension($type);
$fname = $hash . $ext;
Save the file (or optionally recreate it with a library to strip out meta-data) but NEVER in a publicly accessible directory
$upload_path = '/path/to/private/folder';
move_uploaded_file($_FILES['file']['tmp_name'], "$upload_path/$fname");
The steps above are perfectly secure and more than reasonable, of course there is always a risk that some other part of your application or server might be vulnerable.
I would do the following with an apparent image upload:
1) Use is_uploaded_file() to ensure you've not been fooled into working on something else entirely
if(!is_uploaded_file($yourfile))
return false;
2) Check the mimetype with exif_imagetype() and block anything you don't want
$allowed_images = array(IMAGETYPE_BMP, IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG);
$uType = exif_imagetype($yourfile);
if(!in_array($uType, $allowed_images))
{
unlink($yourfile);
return false;
}
3) Use Imagick to remake the image and remove all comments and metadata:
$image = new Imagick($yourfile);
$image->resizeImage($image->getImageWidth(), $image->getImageHeight(), Imagick::FILTER_CATROM, 1);
$image->stripImage(); // remove all comments and similar metadata
4) Write the replacement image to the filesystem and erase the original file:
$image->writeImage("/path/to/new/image");
unlink($yourfile);
5) Upload this image to S3.
// your S3 code here
6) Make a note of the image's S3 URL in the database or wherever.
// your database code here
7) Erase the replacement image.
unlink("/path/to/new/image");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With