If this is a newby question, forgive me. I have coded a php file uploader. After hearing that hackers could attatch/disguise code to images to infect sites, I got an idea to solve that problem. To convert the upload image to another file format (png) and then to another (jpg) with 70% quality. This caused the malware to become corrupted. The problem is, this total conversion process takes a about 1 minute at top speed. The service I'm making needs to be quick to handle the file uploads so that the users can go about the work. How can I speed up this process? The upload code is below (important variables are blanked).
// upload the file
$status = "...recieving the file...";
move_uploaded_file($_FILES["file"]["tmp_name"], "$folder" . $_FILES["file"]["name"]);
$status = "...processing the file...";
// convert the file to destroy viruses
$filename21 = "$folder" . $_FILES["file"]["name"];
imagepng(imagecreatefromstring(file_get_contents($filename21)), "$folder"."debug.".$picture_newname.".png");
$imageTmp=imagecreatefrompng("$folder"."debug.".$picture_newname.".png");
imagejpeg($imageTmp, "$folder".$picture_newname.".jpg", 90);
imagedestroy($imageTmp);
These are the steps it follows:
Scan database for file with the same name
if file with same name is found, rename the current upload
receive the file
"evaluate" the file (the double conversion process to protect the server)
insert the info into the uploads database
If any other codes are needed (or if i should do some more timing) please let me know. Thanks in advance for your assistance.
This is a crazy idea. You're not just tying up the server in converting between image formats, you're also degrading the quality of every uploaded image.
I'd recommend a different approach
When a file is uploaded, use PHP's getimagesize() function to check the image. If this function returns FALSE (or an unexpected image type, or strange dimensions, etc.), then the file is corrupt and can be deleted.
Use exiftool or something similar to strip away all the metadata from the uploaded file before you store it away on the server. That way you can ensure that the file only contains image data.
Perhaps you could check that the value of $_FILES["file"]["name"] doesn't contain anything sneaky like ../../ before you use it to save the file on your server.
It's totally bad idea implement double conversion for security purpose, because of DoS attack.
Balanced solution between speed & security must contain:
Check MIME type.
Check file extension.
Check file size. (highly recommended)
Check image size. (optional, depends on application requirements)
Something like this:
$allowable_types = array(
'png' => 'image/png',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif'
);
$max_file_size = 10240; // 10K limit example
$finfo = new finfo(FILEINFO_MIME_TYPE);
$type = $finfo->file($_FILES['name']['tmp_name']);
$size = filesize($_FILES['name']['tmp_name']);
$info = pathinfo($_FILES['name']['tmp_name']);
if (isset($allowable_types[$info['extension']])
and $type === $allowable_types[$info['extension']]
and $size <= $max_file_size
) {
// check image size if your app require this
move_uploaded_file($_FILES['name']['tmp_name'], $destination);
}
Update
I would not recommend to use $_FILES['file']['name'] in destination path or scan whole directory for same name. Because of some security flaws and performance drop. Better solution is to generate unique name for each image:
$new_name = uniquid() . '.' . $info['extension'];
$destination = $upload_path . $new_name;
Related
I try to use lazyload but it's load image poorly so I want to use thumbnail so it can load faster and save bandwidth. There's a lot of source code that I can use for reducing image data and resolution, I am trying to merge it with my upload.php but it doesn't work, I'm trying move_upload_path and copy below move_upload_path, it work and storing in more than one folder but it's not showing up on my MySQL database. Here's my code (I'm not using the new one that storing in multiple folder path)
<?php
echo "<pre>";
print_r($_FILES['my_image']);
echo "</pre>";
$img_name = $_FILES['my_image']['name'];
$img_size = $_FILES['my_image']['size'];
$tmp_name = $_FILES['my_image']['tmp_name'];
$error = $_FILES['my_image']['error'];
if ($error === 0) {
if ($img_size > 125000000) {
$em = "Sorry, your file is too large.";
header("Location: index1.php?error=$em");
}else {
$img_ex = pathinfo($img_name, PATHINFO_EXTENSION);
$img_ex_lc = strtolower($img_ex);
$allowed_exs = array("jpg", "jpeg", "png");
if (in_array($img_ex_lc, $allowed_exs)) {
$new_img_name = uniqid("IMG-", true).'.'.$img_ex_lc;
$img_upload_path = 'uploads/'.$new_img_name;
move_uploaded_file($tmp_name, $img_upload_path);
$sql = "INSERT INTO images(image_url)
VALUES('$new_img_name')";
mysqli_query($conn, $sql);
header("Location: index.php");
}else {
$em = "You can't upload files of this type";
header("Location: index.php?error=$em");
}
}
}else {
$em = "unknown error occurred!";
header("Location: index.php?error=$em");
}
Php's Upload is for a one-time copy from a remote site to your site.
Lazyload, <picture>, and WebP are for speeding up the rendering of your page to users.
INSERTing images into your database is [usually] the wrong approach. Instead, store the image on disk and INSERT the URL pointing to it into the database. Then use <img...> to allow for HTML to fetch the image in parallel after the text on the page has been loaded. This, then, allows for lazyload, etc. to be used.
It is possible to have an image 'inline' by using base64 encoding and using as 'data' in <img...>. This might be useful for thumbnails, but I have decided that it is rarely worth the hassle.
PHP has the "image*()" routines for scaling (etc) images. I would use these for a one-time transformation to a different size (for <picture>) or better compression (WebP) or thumbnail.
It is also possible to use a PHP script to dynamically build an image. I would not use this for repeated build. I use something like this to experiment with different croppings, etc: <img src=".../crop.php?w=...>.
<img ... height=123 width=234> is an easy way to make a thumbnail (or other size) of a picture from the original. However, it requires the client to download the original, then, change the height and width, so it is not optimal for performance.
When storing an image in MySQL, be sure to use, say, MEDIUMBLOB. That it is limited to 16MB (not the 120MB that you are checking for). I don't know if MySQL can INSERT a blob bigger than that, in spite of having LONGBLOB which theoretically can hold 4GB. I hesitate because of various other limits in the encoding and transmission of the image from client to server.
I suspect that PNG files are bigger than the equivalent JPG. If space and download speed mater more than "lossy", consider converting PNGs to JPGs. Or even test the relative sizes when you initially acquire (Upload) each PNG.
If you will be storing images on your machine and serving from your machine, be aware of the web server's (Apache?) limitations on where things can be placed.
Is the way i am handling image uploads secure? Is there ANY way someone could upload a .php file, or some other file which can somehow execute php code (even if the attacker would know the actual file path after the upload?)
function random($longueur = 10)
{
return substr(str_shuffle(str_repeat($x='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($longueur/strlen($x)) )),1,$longueur);
}
$random = random(5);
//POST DATA
$img_name = htmlspecialchars($_POST["img_name"]);
//IMAGE
mkdir('../../assets/images/'.$random.'/');
$target_dir = '../../assets/images/'.$random.'/';
$target_file = $target_dir . basename($_FILES["img_src"]["name"]);
$imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));
//ARRAY EXTENSION GOOD
$extension_autorisee = array('.png', '.jpg', '.jpeg', 'png', 'jpg', 'jpeg');
if (in_array($imageFileType, $extension_autorisee)){
//MOVE IMAGE
move_uploaded_file($_FILES["img_src"]["tmp_name"], $target_file);
$filename = $random."/".$_FILES['img_src']['name'];
}
It appears reasonably secure, the only thing is that it doesn't check MIME types. I don't really know for what purpose you are storing these files and most modern browsers will just throw an error when they receive an image with HTML embedded in it, but it's still something to look into.
Also I'm not an expert in cryptography but you might want to look into the "randomness" of that string. This might be a good resource. Cryptographically Secure Random String Function
Apart from that it looks fine.
Lightning edit: This kind of questions are, I think, more suited for https://codereview.stackexchange.com/
I am trying to take a pic upload from a mobile device to a server. We are building with PhoneGap (Javascript), so we are having turn it into a string in order to send it to the server. I am having problems once I receive it, to turn it back into a readable image file.
Simply put, I need to take a string and a file name sent to me, decode it, convert it into a .png, then crop it into a circular image.
This is what I have going on currently
if (isset($_POST['file']))
{
//Result variable
$result = false;
$pic = base64_decode($_POST['file']);
$filename = $_POST['filename'];
if (strlen($pic) > 9 )
{
$fh = fopen("/var/www/pics/events/".$filename, 'w') or die("can't open file");
fwrite($fh, $pic);
fclose($fh);
}
}
I think I can get the rest of the code to work if I can figure out what I am doing wrong here that makes it not save properly as a image file? The file uploads correctly, but it stores with out an extension, and when I point to it in my browser, it comes up like it is supposed to be an image file, but never displays the image. That little broken picture icon with the colored shapes is what I get when I direct to it's location.
Do I need to be aware of what image type is being sent during this process at all? How is it knowing if it is a .gif, .jpg/jpeg, .png, etc...?
Thanks in advance for any help!
Nathan
For Security reasons you should sanitize the file name to prevent directory traversal.
On a brighter note, make sure the file is saved with the proper extension; if you are already saving with the correct extension you could have an encoding issue from the app.
If neither of the previous possibilities are the case make sure that your String Size does not exceed the maximum POST size limit in your php.ini; if that is the case increase the size limit.
I know this topic is widely talked about. I've done my research, and decided to store image files onto the server instead of the DB as a blob file. The problem that I am having is trying to figure out the best way to upload the file, store it in a designated folder then storing the destination in the db..say../img/something.jpg - I've tried looking through many resources but most of them are missing some important steps in the process.
Problems:
Finding a secure way for uploading the img file
Limiting the file
size Uploading the image to a destination file
Storing the destination file as a text in the DB
I'm using PHP and MySQL.
Dunno what all your points about, but what you really have to be concerned with is
check for the file extension.
extract it from the filename and compare with allowed ones.
also it would be good to check filename to have only one dot, or at least it doesn't have a name like name.html.jpg, due to some odd Apache behavior.
check for the file contents. the best way would be to create a brand new image out of the uploaded one.
take usual precautions while working with DB.
Here you go, this covers the basic ideas of what you want to do:
<?php
$allowedTypes = array("image/jpg", "image/jpeg", "image/png");
$maxSize = 3 * 1024 * 1024; // 3Mb
$fileType = $_FILES["file"]["type"];
$fileSize = $_FILES["file"]["size"];
// check if there was an error
if ($_FILES["file"]["error"] > 0)
{
die($_FILES["file"]["error"]);
}
// check if the filetype is valid
if (!in_array($fileType, $allowedTypes))
{
die("Invalid file type: $fileType");
}
// check if the size doesn't exceed the limitations
if ($fileSize > $maxSize)
{
die("The file was too big: $fileSize");
}
$name = $_FILES["file"]["name"];
$tmpfile = $_FILES["file"]["tmp_name"];
// check if the filename is valid
if (preg_match("/[\w-]+\.(jpg|jpeg|png)$/", $name) != 1)
{
die("Invalid file name: $name");
}
// create unique name if needed
$path = "/var/www/images/" . $name;
move_uploaded_file($tmpfile, $path);
// add the filepath to mysql
mysql_connect("localhost", "username", "password");
mysql_select_db("imagedb");
mysql_query("INSERT INTO images (Location, Size) VALUES ('$path', '$size');");
?>
This is meant to show how it could be done.
read this
personally I'd use imgur which is used here on stackexchange websites
I am creating file upload script and I'm looking for the best techniques and practices to validate uploaded files.
Allowed extensions are:
$allowed_extensions = array('gif','jpg','png','swf','doc','docx','pdf','zip','rar','rtf','psd');
Here's the list of what I'm doing.
Checking file extension
$path_info = pathinfo($filename);
if( !in_array($path_info['extension'], $allowed_extensions) ) {
die('File #'.$i.': Incorrent file extension.');
}
Checking file mime type
$allowed_mimes = array('image/jpeg','image/png','image/gif','text/richtext','multipart/x-zip','application/x-shockwave-flash','application/msword','application/pdf','application/x-rar-compressed','image/vnd.adobe.photoshop');
if( !in_array(finfo_file($finfo, $file), $allowed_mimes) ) {
die('File #'.$i.': Incorrent mime type.');
}
Checking file size.
What should I do to make sure uploaded files are valid files? I noticed strange thing. I changed .jpg file extension to .zip and... it was uploaded. I thought it will have incorrect MIME type but after that I noticed I'm not checking for a specific type but if a specific MIME type exist in array. I'll fix it later, that presents no problems for me (of course if you got any good solution/idea, do not hesitate to share it, please).
I know what to do with images (try to resize, rotate, crop, etc.), but have no idea how to validate other extensions.
Now's time for my questions.
Do you know good techniques to validate such files? Maybe I should unpack archives for .zip/.rar files, but what about documents (doc, pdf)?
Will rotating, resizing work for .psd files?
Basically I thought that .psd file has following mime: application/octet-stream but when
I tried to upload .psd file it showed me (image/vnd.adobe.photoshop). I'm a bit confused about this. Do files always have the same MIME type?
Also, I cannot force code block to work. Does anyone have a guess as to why?
Lots of file formats have a pretty standard set of starting bytes to indicate the format. If you do a binary read for the first several bytes and test them against the start bytes of known formats it should be a fairly reliable way to confirm the file type matches the extension.
For example, JPEG's start bytes are 0xFF, 0xD8; so something like:
$fp = fopen("filename.jpg", "rb");
$startbytes = fread($fp, 8);
$chunked = str_split($startbytes,1);
if ($chunked[0] == 0xFF && $chunked[1] == 0xD8){
$exts[] = "jpg";
$exts[] = "jpeg";
}
then check against the exts.
could work.
If you want to validate images, a good thing to do is use getimagesize(), and see if it returns a valid set of sizes - or errors out if its an invalid image file. Or use a similar function for whatever files you are trying to support.
The key is that the file name means absolutely nothing. The file extensions (.jpg, etc), the mime types... are for humans.
The only way you can guarantee that a file is of the correct type is to open it and evaluate it byte by byte. That is, obviously, a pretty daunting task if you want to try to validate a large number of file types. At the simplest level, you'd look at the first few bytes of the file to ensure that they match what is expected of a file of that type.