Image and PDF file upload validation in PHP - php

Im creating a web app that manages staff records, one of the features allows staff to upload copies of their qualifications. Because many scanners output into different file types i need to be able to validate that the uploaded files are either a PDF or one of the many different image file types.
Im aware of the MIME validation method and have used this but as it is relatively easy to hack I'm looking for a more secure method to supplement it with.
The relevant code from the config file where constants are stored
// Set the file types allowed to be uploaded
define('FILETYPEALLOWED', serialize (array ('image/pjpeg', 'image/jpeg', 'image/JPG', 'image/X-PNG', 'image/PNG', 'image/png', 'image/x-png' ) ));
The code that handles the form
//If a file was uploaded, Move the uploaded file
IF (isset($_FILES['upload'])) {
$allowed = unserialize (FILETYPEALLOWED);
$fileinfo = finfo_open (FILEINFO_MIME_TYPE);
//IF file type is allowed
IF (in_array($_FILES['upload'] ['type'], $allowed )) {
move_uploaded_file($_FILES['upload'] ['tmp_name'], UPLOADS . $_FILES['upload'] ['name'] );
} else {
//Add an error to the $errors array
$errors[] = 'The file type is not allowed.';
}
//Delete the temporary file
IF (file_exists ($_FILES['upload'] ['tmp_name']) && is_file($_FILES ['upload'] ['tmp_name'])) {
unlink ($_FILES ['upload'] ['tmp_name']);
echo '<p>The temp file been file.</p>';
}
} else {
print '<p>no file was uploaded</p>';
}

Related

Cannot upload image via move_uploaded_file

I am using code below to upload image via drag an drop system. Everything is ok, JS part is working well. But PHP part of code shows me error:
PHP Warning: move_uploaded_file(assets/img/photos/1657614494809.jpg): failed to open stream: No such file or directory in /www_root/_inc/upload.php on line 29, referer: https://smartobchod.sk/bazar/add.php
[Sat Jul 23 12:56:18 2022] [error] [client 78.99.32.1] PHP Warning: move_uploaded_file(): Unable to move '/home/gm016900/tmp/phpKaBL6m' to 'assets/img/photos/1657614494809.jpg' in /www_root/_inc/upload.php on line 29, referer: https://smartobchod.sk/bazar/add.php
EDITED 25.07.2022 (added some security checks)
My PHP code in upload.php is:
<?php
// Get reference to uploaded image
$image_file = $_FILES["file"];
// Get image name
$image_name = $_FILES["file"]["name"];
// Get file size
$image_size = $_FILES["file"]["size"];
// Exit if no file uploaded or image name contains unvalid characters /, \\
if ( ( !strpos($image_name, '/') || !strpos($image_name, '\\') ) && isset($image_file) ) {
$errors = array();
$maxsize = 10485760;
$acceptable = array(
'image/jpeg',
'image/jpg',
'image/gif',
'image/png'
);
} else {
die('No image uploaded.');
}
// Exit if image file is zero bytes or if image size is more then 10 MB
if (getimagesize($image_file["tmp_name"]) <= 0) {
die('Uploaded file has no contents.');
} elseif ($image_size >= $maxsize) {
die('Image is too large. Image must be less than 10 megabytes.');
}
// Exit if is not a valid image file or image has not supported type
$image_type = exif_imagetype($image_file["tmp_name"]);
if (!$image_type) {
die('Uploaded file is not an image.');
} elseif ( !in_array($image_file["type"], $acceptable) ) {
die('Image has not supported type JPG, PNG, GIF.');
} else {
$src = "default.png";
}
// Get file extension based on file type, to prepend a dot we pass true as the second parameter
$image_extension = image_type_to_extension($image_type, true);
// Create a unique image name
$image_name = bin2hex(random_bytes(16)) . $image_extension;
// Location
$relative_location = "/bazar/assets/img/photos/".$image_name;
$absolute_location = dirname(__DIR__, 2).$relative_location;
$return_arr = array();
// transfer file created in tmp folder to location of pictures with name saved in address of $location
// in $image_file is stored $_FILES["file"]["tmp_name"]
if (move_uploaded_file($image_file["tmp_name"], $absolute_location)) {
$src = $relative_location;
$return_arr = array("name" => $image_name,"size" => $image_size, "src"=> $src);
}
echo json_encode($return_arr);
This is sent in header to upload.php:
Content-Disposition: form-data; name="file"; filename="1657614494809.jpg"
Content-Type: image/jpeg
Can you advice me what can be problem?
Here I think your tmp/ folder permission is not proper so it is not able to read that folder or write just check /home/gm016900/tmp permission is should be gm016900 or it may not be created.
Based on your comments, you just need to keep track of relative vs absolute paths.
I wasn't able to test this code, but hopefully you should get the general gist if it fails. Instead of a single $location variable, I'm using two variables $relativeLocation and $absoluteLocation. The latter isn't actually needed, but it makes debugging much easier.
/* Getting file name */
$filename = $_FILES['file']['name'];
/* Getting File size */
$filesize = $_FILES['file']['size'];
/* Location */
$relativeLocation = "assets/img/photos/".$filename;
$absoluteLocation = __DIR__.'/'.$relativeLocation;
$return_arr = array();
/* Upload file */
if (move_uploaded_file($_FILES['file']['tmp_name'], $absoluteLocation)) {
$src = "default.png";
// checking file is image or not
if (is_array(getimagesize($absoluteLocation))) {
$src = $relativeLocation;
}
$return_arr = array("name" => $filename, "size" => $filesize, "src" => $src);
}
echo json_encode($return_arr);
Depending on where your function lives relative to the storage, you might need to go update a directory or two, or possibly go up and then back down:
// Up one directory
$absoluteLocation = dirname(__DIR__).'/'.$relativeLocation;
// Up two directories
$absoluteLocation = dirname(__DIR__, 2).'/'.$relativeLocation;
// Up one directory then over to a sibling directory
$absoluteLocation = dirname(__DIR__).'/files/'.$relativeLocation;
As a personal preference, I always have a constant (or equivalent) in my projects that represents a known location on disk, and if I'm using a router script I define it there. That way, no matter how deep I get in nesting I can build my paths relative to that constant.
I also go one step further and use this library (merged into Symfony here) to avoid string concatenation, not worry about whether something ends with a slash or not, and to be more cross platform. So my actual code looks like:
$absoluteLocation = Path::join(PROJECT_ROOT, 'files', $relativeLocation);
Some people might think that's overkill, but once again it is just a personal preference.

In Codeigniter How to check if the uploaded file is actually a pdf or jpg or png?

In Codeigniter How to check if the uploaded file is actually a pdf or jpg or png? Because, if we upload an .exe file with .pdf extension then also it gets uploaded without any problem. So, it there a proper way to actually check the file and its content to be able to determine whether it is actually a pdf or exe. Because with just the file extension anything can be uploaded. Please, help me find a proper solution for this. Is there any native php function through which we can achieve this. If so a sample code might be helpful.
you can use mime_content_type() function which is in built in php it provides actual content type even if the extension is changed
php docs
<?php
echo mime_content_type('abcd.pdf') //application/pdf
?>
checking mime_content_type while uploading
$mimetype = mime_content_type($_FILES['file']['tmp_name']);
if(in_array($mimetype, array('image/jpeg', 'image/gif', 'image/png'))) {
move_uploaded_file($_FILES['file']['tmp_name'], '/whatever/something/imagedir/' . $_FILES['file']['name']);
echo 'OK';
} else {
echo 'It is not an image';
}
You need to check real file type and given file type like this:
$config['upload_path'] = './uploads/';
$config['allowed_types'] = 'gif|jpg|png|pdf';
$this->load->library('upload', $config);
$file = $_FILES['userfile'];
// given file type
$gftype=pathinfo($file['name'], PATHINFO_EXTENSION);;
// real file type
$rftype = explode('/',mime_content_type($file['tmp_name']))[1];
if($gftype === $rftype){
if (! $this->upload->do_upload('userfile')){
echo "Error";
}else{
echo "Success";
}
}else{
echo 'This is not real extension';
}

How to validate multiple uploaded file using php?

I am using PHP to upload multiple file to the database. So when I check file extension its always showing me my given error message even if file extension is correct:
File type is not allowed, We accept only .jpg, .png and .gif extension
file
Here is the validation :
$total = count($_FILES['client_doc']['name']);
for($i=0; $i<$total; $i++) {
$file_name = htmlspecialchars($_FILES['client_doc']['name'][$i]);
$file_tmp = htmlspecialchars($_FILES['client_doc']['tmp_name'][$i]);
$file_size = htmlspecialchars($_FILES['client_doc']['size'][$i]);
$file_ext = explode('.', $file_name);
$file_ext = strtolower(end($file_ext));
$allowed_type = array('jpg', 'jpeg', 'gif', 'png');
}
if(!empty($file_name)) {
if(!in_array($file_ext, $allowed_type)) {
$msg[] = 'File type is not allowed, We accept only .jpg, .png and .gif extension file';
$msg['error'] = true;
}elseif($file_size > 2097152) { // only 2 mb size is allowed
$msg[] = 'Uploaded file name must be less than 2MB';
$msg['error'] = true;
}
}
I've tested your code by giving the $file_name variable test.png value and it works fine. Are you sure your file form works correctly? Try to echo $file_name's at the end of for loop and check if there's any output.
Another tips:
Do you want to validate all the files? As far as I'm concerned your current code validates only the last file - you iterate through all the files, but you overwrite variables storing file's data. In order to validate all the elements you have to put your condition inside the for loop.
Because your $allowed_type is a constant, you don't have to overwrite it inside the loop.

Only allow image files to be uploaded to my server with PHP

I'm trying to make a script in which I only allow .png, .jpeg and .gif files to be uploaded, based on MIME types. What I have so far is this:
if(file_exists($root."/upload/gallery/".$_FILES["image"]["name"]))
{
$filename = explode(".",$_FILES['image']['name']);
$randomnumber = rand(0, 10000);
$imageName = $filename[0].$randomnumber.".".$filename[1];
}
else
{
$imageName = $_FILES['image']['name'];
}
$image = mysql_real_escape_string(htmlspecialchars("/upload/gallery/".$imageName));
$allowed = array('image/jpeg', 'image/png', 'image/gif');
if(in_array($_FILES['image']['name'], $allowed)){
echo "Allowed!";
die;
}
else {
echo "Not allowed!";
die;
}
I was almost certain this should work. But it always echoes Not allowed! while I choose files with the correct MIME type, what am I doing wrong here? The code includes a check for files in my upload folder that already have the same name and if so adds a random number to the filename.
You are comparing the allowed list against the file name, not the type.
The type of the file will be contained in an array of applicable types in:
$_FILES['image']['type']

How to make auto updating image gallery?

Well I have a upload script. What I need is how would I go about making it auto upload to a page on my site where it can be displayed as an image gallery?
Code:
<?php
// Configuration - Your Options
$allowed_filetypes = array('.jpg','.gif','.bmp','.png','.jpeg'); // These will be the types of file that will pass the validation.
$max_filesize = 1000000; // Maximum filesize in BYTES (currently 0.5MB).
$upload_path = './images/uploaded_images/'; // The place the files will be uploaded to (currently a 'files' directory).
$filename = $_FILES['userfile']['name']; // Get the name of the file (including file extension).
$ext = substr($filename, strpos($filename,'.'), strlen($filename)-1); // Get the extension from the filename.
// Check if the filetype is allowed, if not DIE and inform the user.
if ( ! in_array($ext, $allowed_filetypes))
die('The file you attempted to upload is not allowed.');
// Now check the filesize, if it is too large then DIE and inform the user.
if (filesize($_FILES['userfile']['tmp_name']) > $max_filesize)
die('The file you attempted to upload is too large.');
// Check if we can upload to the specified path, if not DIE and inform the user.
if ( ! is_writable($upload_path))
die('You cannot upload to the specified directory, please CHMOD it to 777.');
// Upload the file to your specified path.
if (move_uploaded_file($_FILES['userfile']['tmp_name'],$upload_path . $filename))
echo 'Your file upload was successful, view the file here'; // It worked.
else
echo 'There was an error during the file upload. Please try again.'; // It failed :(.
?>
Any Ideas?
One approach would be to scan the directory you are storing the images and write html for each file you encounter. Take a look at the scandir function. Some rudimentary code to get you started:
$upload_path = './images/uploaded_images/';
$files = scandir($upload_path);
foreach($files as $filename) {
if(is_image($filename)) {
echo "<div class='gallery-image'><img src='{$filename}'/></div>";
}
}
function is_image($filename) {
$image_extensions = array('jpg', 'jpeg', 'png', 'gif');
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
return in_array($ext, $image_extensions);
}

Categories