I am new here, nice to meet you.
I'm trying create a clean copy of a picture uploaded by a user after security checks, and to save this copy in a designated folder and in my DB. I'm working with XAMPP on Windows 10 and I granted the right permissions for the said folder. My code is below:
/*bit of code for picture addition, in a function for album publication*/
if($_FILES['albumCover']['error'] === 0) {
$covType;
$createCover;
$covName = guidv4();
$imgRegex = '/^[a-z\d][^.]*\.(jpe?g$)|\.(png$)/i';
$covInfo = finfo_open(FILEINFO_MIME_TYPE);
$covMime = finfo_file($covInfo, $_FILES['albumCover']['tmp_name']);
finfo_close($covInfo);
if(!preg_match($imgRegex, $_FILES['albumCover']['name']) ||
str_contains($_FILES['albumCover']['name'], ' ') ||
in_array($_FILES['albumCover']['type'], ['image/jpeg','image/png'])||
strpos($covMime, 'image/') !== 0)
{
$addMessages['errors'][] = 'Caractère autorisé pour le nom d\'image : lettres sans
accent / sans trémas / sans cédille, chiffres, tirets et underscores. Fichiers
autorisés : images .jpg, .jpeg ou .png.';
}
if(empty($addMessages['errors'])) {
if(preg_match($imgRegex, $_FILES['albumCover']['name'])
&& $_FILES['albumCover']['type'] == 'image/jpeg'
&& strpos($covMime, 'image/') !== 0)
{
$covType = 'jpeg';
$createCover = imagecreatefromjpeg($_FILES['albumCover']['tmp_name']);
imagejpeg($createCover,
self::COV_SECURE_PATH.$covName.'.'.pathinfo($_FILES['albumCover']['name'],
PATHINFO_EXTENSION)
);
move_uploaded_file($_FILES['albumCover']['tmp_name'],
self::COV_SECURE_PATH.'/'.$_FILES['albumCover']['name']);
} elseif(preg_match($imgRegex, $_FILES['albumCover']['name'])
&& $_FILES['albumCover']['type'] == 'image/png'
&& strpos($covMime, 'image/') !== 0)
{
$covType = 'png';
$createCover = imagecreatefrompng($_FILES['albumCover']['tmp_name']);
imagepng($createCover,
self::COV_SECURE_PATH.$covName.'.'.pathinfo($_FILES['albumCover']['name'],
PATHINFO_EXTENSION));
move_uploaded_file($_FILES['albumCover']['tmp_name'],
self::COV_SECURE_PATH.'/'.$_FILES['albumCover']['name']);
}
}
}
A few more details: the bit of code above is part of a class method. The constant COV_SECURE_PATH is declared in the class, before the method. And the guidv4() function for the picture name is in the same file, before the declaration of the class.
I have no PHP error messages, my prints display the right name and extension of each file and I declared the attribute enctype="multipart/form-data" in my HTML form. The pictures are saved in my DB (I will add the code regarding the DB if needed), but they won't be saved in my folder and I don't know what I'm missing. So far I haven't found an answer when searching in any site, any help will be very appreciated.
Well, I have finally fixed everything. My error was: if(preg_match($imgRegex, $_FILES['albumCover']['name']) && $_FILES['albumCover']['type'] == 'image/jpeg' && strpos($covMime, 'image/') !== 0) {
instead of if(preg_match($imgRegex, $_FILES['albumCover']['name']) && $_FILES['albumCover']['type'] == 'image/jpeg' && strpos($covMime, 'image/') === 0) {
And I also realised that I hadn't activated GD in my php.ini, now it's done.
Thanks to everyone who answered me!
take a look at my image validation...
//connecting to the database
$mysqli = mysqli_connect( 'localhost', 'root', '', 'img_validation' )
or
die( 'Database connection error => '.mysqli_connect_error() );
//errors and success message handling
$errors = array();
$success = array();
//imgs dir
$imgs_dir = './assets/imgs/';
//if upload button is pressed...
if ( isset( $_POST['upload'] ) ) {
//get image info...
$imgName = $_FILES['image']['name'];
$imgTmp = $_FILES['image']['tmp_name'];
$imgSize = $_FILES['image']['size'];
//image validation
if ( $imgName ) {
//get extension info
$imgExt = strtolower( pathinfo( $imgName, PATHINFO_EXTENSION ) );
//allowed image extensions
$allowExt = array( 'jpg', 'jpeg', 'png' );
//create new random image name
$uploadedPic = 'user-upload-'.rand( 100000, 999999 ).'.'.$imgExt;
//validate image extension
if ( in_array( $imgExt, $allowExt ) ) {
//max image upload size 3MB
if ( $imgSize < 3000000 ) {
//move image file to designated directory/folder
move_uploaded_file( $imgTmp, $imgs_dir.$uploadedPic );
} else {
array_push( $errors, "Image too large. Max. upload size 3MB." );
}
} else {
array_push( $errors, "<script type='text/javascript'>
alert('JPG, JPEG and PNG formats required.\\nMax. upload size 3MB.');</script>" );
array_push( $errors, "Please select a valid image." );
}
} else {
$uploadedPic;
}
//if found no errors...
if ( count( $errors ) == 0 ) {
//insert new record in `user_uploads` table in the database...
$admin = mysqli_query( $mysqli, "INSERT INTO user_uploads (image) VALUES ('$uploadedPic');" );
if ( $admin == 1 ) {
//success message
array_push( $success, "Image uploaded successfully." );
//redirect to profile after 2 seconds...
header( 'refresh: 2; url=http://localhost/img-validation/' );
} else {
array_push( $errors, mysqli_error() );
}
}
}
When I was at the early learning stage of PHP, I write this code and hope this will work for you : )
I am making an image upload function which I can re-use in my code, which has to be 100% secure. Please tell me if you can spot and security holes in my initial code;
function Upload($file)
{
list($width,$height,$type,$attr) = getimagesize($file);
$mime = image_type_to_mime_type($type);
if(($mime != "image/jpeg") && ($mime != "image/pjpeg") && ($mime != "image/png"))
{
return 'Error3: Upload file type un-recognized. Only .JPG or .PNG images allowed';
}else{
$Newname = md5('sillysalt'.time());
if (move_uploaded_file($file, 'images/'.$Newname.$type))
{
return 'Uploaded!';
}else{
return 'Server Error!';
}
}
}
UPDATE This is how far I've gotten with your help and some research, please tell me what you think. I don't mind much about the speed, for me it's all about being 100% secure, or as close to.
function Upload($file)
{
list($width,$height,$type,$attr) = getimagesize($file);
$mime = image_type_to_mime_type($type);
$folder = 'images/';
// mime checks add a layer of security that keeps out less sophisticated attackers
if(($mime != "image/jpeg") && ($mime != "image/pjpeg") && ($mime != "image/png"))
{
return 'Error3: Upload file type un-recognized. Only .JPG or .PNG images allowed';
}else{
// If the file has no width its not a valid image
if(!$width)
{
$Newname = md5('sillysalt'.time());
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime2 = finfo_file($finfo, $folder.$Newname);
// Should I remove this second mime check? since the info comes form the same spoofable source in the image
if(($mime != "image/jpeg") && ($mime != "image/pjpeg") && ($mime != "image/png"))
{
$fileType = exif_imagetype($file);
$allowed = array(IMAGETYPE_JPEG, IMAGETYPE_PNG);
if(!in_array($fileType, $allowed))
{
// don't overwrite an existing file
$i = 0;
$parts = pathinfo($file);
while(file_exists($folder . $name))
{
$i++;
$name = $Newname."-".$i.".".$parts["extension"];
}
if(move_uploaded_file($file, $folder.$name))
{
// set good permissions for the file
chmod($name, 0644);
return 'Uploaded!';
}else{
return 'Server Error!';
}
}
}
}
}
}
As long as you don't use the FileInfo (http://www.php.net/manual/en/ref.fileinfo.php) extensions from php to check the mime type, your function is not secure at all (think later you'll want to upload pdf's, excels, etc).
Also, md5 over md5 does nothing than increasing the collision chances.
L.E: Something as simple as the following should do it:
function getExtensionToMimeTypeMapping() {
return array(
'ai'=>'application/postscript',
'aif'=>'audio/x-aiff',
'aifc'=>'audio/x-aiff',
'aiff'=>'audio/x-aiff',
'anx'=>'application/annodex',
'asc'=>'text/plain',
'au'=>'audio/basic',
'avi'=>'video/x-msvideo',
'axa'=>'audio/annodex',
'axv'=>'video/annodex',
'bcpio'=>'application/x-bcpio',
'bin'=>'application/octet-stream',
'bmp'=>'image/bmp',
'c'=>'text/plain',
'cc'=>'text/plain',
'ccad'=>'application/clariscad',
'cdf'=>'application/x-netcdf',
'class'=>'application/octet-stream',
'cpio'=>'application/x-cpio',
'cpt'=>'application/mac-compactpro',
'csh'=>'application/x-csh',
'css'=>'text/css',
'csv'=>'text/csv',
'dcr'=>'application/x-director',
'dir'=>'application/x-director',
'dms'=>'application/octet-stream',
'doc'=>'application/msword',
'drw'=>'application/drafting',
'dvi'=>'application/x-dvi',
'dwg'=>'application/acad',
'dxf'=>'application/dxf',
'dxr'=>'application/x-director',
'eps'=>'application/postscript',
'etx'=>'text/x-setext',
'exe'=>'application/octet-stream',
'ez'=>'application/andrew-inset',
'f'=>'text/plain',
'f90'=>'text/plain',
'flac'=>'audio/flac',
'fli'=>'video/x-fli',
'flv'=>'video/x-flv',
'gif'=>'image/gif',
'gtar'=>'application/x-gtar',
'gz'=>'application/x-gzip',
'h'=>'text/plain',
'hdf'=>'application/x-hdf',
'hh'=>'text/plain',
'hqx'=>'application/mac-binhex40',
'htm'=>'text/html',
'html'=>'text/html',
'ice'=>'x-conference/x-cooltalk',
'ief'=>'image/ief',
'iges'=>'model/iges',
'igs'=>'model/iges',
'ips'=>'application/x-ipscript',
'ipx'=>'application/x-ipix',
'jpe'=>'image/jpeg',
'jpeg'=>'image/jpeg',
'jpg'=>'image/jpeg',
'js'=>'application/x-javascript',
'kar'=>'audio/midi',
'latex'=>'application/x-latex',
'lha'=>'application/octet-stream',
'lsp'=>'application/x-lisp',
'lzh'=>'application/octet-stream',
'm'=>'text/plain',
'man'=>'application/x-troff-man',
'me'=>'application/x-troff-me',
'mesh'=>'model/mesh',
'mid'=>'audio/midi',
'midi'=>'audio/midi',
'mif'=>'application/vnd.mif',
'mime'=>'www/mime',
'mov'=>'video/quicktime',
'movie'=>'video/x-sgi-movie',
'mp2'=>'audio/mpeg',
'mp3'=>'audio/mpeg',
'mpe'=>'video/mpeg',
'mpeg'=>'video/mpeg',
'mpg'=>'video/mpeg',
'mpga'=>'audio/mpeg',
'ms'=>'application/x-troff-ms',
'msh'=>'model/mesh',
'nc'=>'application/x-netcdf',
'oga'=>'audio/ogg',
'ogg'=>'audio/ogg',
'ogv'=>'video/ogg',
'ogx'=>'application/ogg',
'oda'=>'application/oda',
'pbm'=>'image/x-portable-bitmap',
'pdb'=>'chemical/x-pdb',
'pdf'=>'application/pdf',
'pgm'=>'image/x-portable-graymap',
'pgn'=>'application/x-chess-pgn',
'png'=>'image/png',
'pnm'=>'image/x-portable-anymap',
'pot'=>'application/mspowerpoint',
'ppm'=>'image/x-portable-pixmap',
'pps'=>'application/mspowerpoint',
'ppt'=>'application/mspowerpoint',
'ppz'=>'application/mspowerpoint',
'pre'=>'application/x-freelance',
'prt'=>'application/pro_eng',
'ps'=>'application/postscript',
'qt'=>'video/quicktime',
'ra'=>'audio/x-realaudio',
'ram'=>'audio/x-pn-realaudio',
'ras'=>'image/cmu-raster',
'rgb'=>'image/x-rgb',
'rm'=>'audio/x-pn-realaudio',
'roff'=>'application/x-troff',
'rpm'=>'audio/x-pn-realaudio-plugin',
'rtf'=>'text/rtf',
'rtx'=>'text/richtext',
'scm'=>'application/x-lotusscreencam',
'set'=>'application/set',
'sgm'=>'text/sgml',
'sgml'=>'text/sgml',
'sh'=>'application/x-sh',
'shar'=>'application/x-shar',
'silo'=>'model/mesh',
'sit'=>'application/x-stuffit',
'skd'=>'application/x-koan',
'skm'=>'application/x-koan',
'skp'=>'application/x-koan',
'skt'=>'application/x-koan',
'smi'=>'application/smil',
'smil'=>'application/smil',
'snd'=>'audio/basic',
'sol'=>'application/solids',
'spl'=>'application/x-futuresplash',
'spx'=>'audio/ogg',
'src'=>'application/x-wais-source',
'step'=>'application/STEP',
'stl'=>'application/SLA',
'stp'=>'application/STEP',
'sv4cpio'=>'application/x-sv4cpio',
'sv4crc'=>'application/x-sv4crc',
'swf'=>'application/x-shockwave-flash',
't'=>'application/x-troff',
'tar'=>'application/x-tar',
'tcl'=>'application/x-tcl',
'tex'=>'application/x-tex',
'texi'=>'application/x-texinfo',
'texinfo'=>'application/x-texinfo',
'tif'=>'image/tiff',
'tiff'=>'image/tiff',
'tr'=>'application/x-troff',
'tsi'=>'audio/TSP-audio',
'tsp'=>'application/dsptype',
'tsv'=>'text/tab-separated-values',
'txt'=>'text/plain',
'unv'=>'application/i-deas',
'ustar'=>'application/x-ustar',
'vcd'=>'application/x-cdlink',
'vda'=>'application/vda',
'viv'=>'video/vnd.vivo',
'vivo'=>'video/vnd.vivo',
'vrml'=>'model/vrml',
'wav'=>'audio/x-wav',
'wrl'=>'model/vrml',
'xbm'=>'image/x-xbitmap',
'xlc'=>'application/vnd.ms-excel',
'xll'=>'application/vnd.ms-excel',
'xlm'=>'application/vnd.ms-excel',
'xls'=>'application/vnd.ms-excel',
'xlw'=>'application/vnd.ms-excel',
'xml'=>'application/xml',
'xpm'=>'image/x-xpixmap',
'xspf'=>'application/xspf+xml',
'xwd'=>'image/x-xwindowdump',
'xyz'=>'chemical/x-pdb',
'zip'=>'application/zip',
);
}
function getMimeType($filePath) {
if (!is_file($filePath)) {
return false;
}
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $filePath);
finfo_close($finfo);
return $mime;
}
function upload($filePath, $destinationDir = 'images', array $allowedMimes = array()) {
if (!is_file($filePath) || !is_dir($destinationDir)) {
return false;
}
if (!($mime = getMimeType($filePath))) {
return false;
}
if (!in_array($mime, $allowedMimes)) {
return false;
}
$ext = null;
$extMapping = getExtensionToMimeTypeMapping();
foreach ($extMapping as $extension => $mimeType) {
if ($mimeType == $mime) {
$ext = $extension;
break;
}
}
if (empty($ext)) {
$ext = pathinfo($filePath, PATHINFO_EXTENSION);
}
if (empty($ext)) {
return false;
}
$fileName = md5(uniqid(rand(0, time()), true)) . '.' . $ext;
$newFilePath = $destinationDir.'/'.$fileName;
if(!rename($filePath, $newFilePath)) {
return false;
}
return $fileName;
}
// use it
if (isset($_FILES['something']['tmp_name'])) {
$file = $_FILES['something']['tmp_name'];
$storagePath = 'images'; // this is relative to this script, better use absolute path.
$allowedMimes = array('image/png', 'image/jpg', 'image/gif', 'image/pjpeg');
$fileName = upload($file, $storagePath, $allowedMimes);
if (!$fileName) {
exit ('Your file type is not allowed.');
} else {
// check if file is image, optional, in case you allow multiple types of files.
// $imageInfo = #getimagesize($storagePath.'/'.$fileName);
exit ("Your uploaded file is {$fileName} and can be found at {$storagePath}/{$fileName}");
}
}
Stop filtering it by mime type it is not safe!
Client can send different mime types with different file extensions. So, you need to check file extension.
edit:
I think I have been misunderstood, I wrote the answer to tell that checking mime type to determine file type is not a good way, the best way to determine the file type is checking file extension. So, I don't mean that checking file extension is enough. Either checking only file extension or mime type is not safe way.
What to do?
1-Check mime type
2-Check file extension
3- decode file name
4- check file content consistency (if possible)
5- regenerate file content (if possible)
I know that attackers can bypass first and second way by using "null byte hack" and "mime type bypass"
So, 3,4 and 5 is so important for security.
I am trying out PHP my first actual script, most of it from tutorial :(
Anyway's
I am having a problem on this part
// This is our limit file type condition
if (!($uploaded_type=="text/java")||!($uploaded_type=="file/class")||!($uploaded_type=="file/jar")) {
echo "You may only upload Java files.<br>";
$ok=0;
}
Basically it doesn't allow any files, even those up there
help!
I want the Java files to be allowed only!
EDIT:
Here is the full code
<?php
$target = "upload/";
$target = $target . basename( $_FILES['uploaded']['name']) ;
$uploaded = basename( $_FILES['uploaded']['name']) ;
$ok=1;
//This is our size condition
if ($uploaded_size > 350000) {
echo "Your file is too large.<br>";
$ok=0;
}
// This is our limit file type condition
if (!($uploaded_type=="text/java")||!($uploaded_type=="file/class")||! ($uploaded_type=="file/jar")) {
echo "You may only upload Java files.<br>";
$ok=0;
}
echo $ok; //Here we check that $ok was not set to 0 by an error
if ($ok==0) {
echo "Sorry your file was not uploaded";
}else {
if(move_uploaded_file($_FILES['uploaded']['tmp_name'], $target)) {
echo "The file ". $uploaded ." has been uploaded";
} else {
echo "Sorry, there was a problem uploading your file.";
}
}
?>
You're using an OR... that means the whole statement evaluates as TRUE if ANY of its member arguments are true. Since a file can only be of one type, you're excluding ALL files. What you want is an 'and' match:
if (!($uploaded_type == 'text/java') && !($uploaded_type == ....)) {
^^---boolean and
Pretending that we're working with a file/class file type, then you version reads:
if the (file is not text/java) OR the (file is not file/class) OR the (file is not file/jar)
TRUE FALSE TRUE
TRUE or FALSE or TRUE -> TRUE
Switchign to AND gives you
TRUE and FALSE and TRUE -> FALSE
Only one of your three conditions could possibly be true, so that you end up with:
if (!false || !false || !true)
Which becomes:
if (true || true || false)
So you should either use an && in place of the OR, or use a nicer function to check for multiple things from a set:
if (!in_array($uploaded_type, array("text/java", "file/class","file/jar")) {
So the if will succeed if neither of the allowed values is found.
You can make this more flexible using in_array():
$allowed_types = array("text/java", "file/class", "file/jar");
if(!in_array($uploaded_type, $allowed_types)) {
echo "You're not allowed to upload this kind of file.<br />";
$ok = 0;
}
This makes it very easy to allow more file types later on. If you want to allow "text/html", you just need to add it to the array and do not need to create so many checks. You could even store the allowed types in a config file or a table in a database and create the array $allowed_types dynamically.
For client file format limit, refer to this Limit file format when using ?
<input type="file" accept="image/*" /> <!-- all image types -->
<input type="file" accept="audio/*" /> <!-- all audio types -->
For server, you can filter the uploaded file by this,
if(in_array(mime_type($file_path),$allowed_mime_types)){
// save the file
}
$allowed_mime_types = array(
'image/jpeg',
'image/jpg',
'image/png',
'image/gif',
'video/mp4'
);
/*
For PHP>=5.3.0, you can use php's `finfo_file`([finfo_file](https://www.php.net/manual/en/function.finfo-file.php)) function to get the file infomation about the file.
For PHP<5.3.0, you can use your's system's `file` command to get the file information.
*/
function mime_type($file_path)
{
if (function_exists('finfo_open')) {
$finfo = new finfo(FILEINFO_MIME_TYPE, null);
$mime_type = $finfo->file($file_path);
}
if (!$mime_type && function_exists('passthru') && function_exists('escapeshellarg')) {
ob_start();
passthru(sprintf('file -b --mime %s 2>/dev/null', escapeshellarg($file_path)), $return);
if ($return > 0) {
ob_end_clean();
$mime_type = null;
}
$type = trim(ob_get_clean());
if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\.]+)#i', $type, $match)) {
$mime_type = null;
}
$mime_type = $match[1];
}
return $mime_type;
}
How to fetch the images from a folder and display it in the page, whether i can resize it in php itself or i have to resize it and upload it separtely to display it as thumbnails?
Here's the basic structure for traversing a directory and doing something with the image files (given 'images' is a directory in the same directory of your script)
$image_types = array(
'gif' => 'image/gif',
'png' => 'image/png',
'jpg' => 'image/jpeg',
);
foreach (scandir('images') as $entry) {
if (!is_dir($entry)) {
if (in_array(mime_content_type('images/'. $entry), $image_types)) {
// do something with image
}
}
}
From here, you can send the images directly to browser, generate tags for HTML page or create thumbnails with GD functions and store them for displaying.
i think this may help you!
<?
$string =array();
$filePath='directorypath/';
$dir = opendir($filePath);
while ($file = readdir($dir)) {
if (eregi("\.png",$file) || eregi("\.jpg",$file) || eregi("\.gif",$file) ) {
$string[] = $file;
}
}
while (sizeof($string) != 0){
$img = array_pop($string);
echo "<img src='$filePath$img' width='100px'/>";
}
?>
eregi is deprecated now so you can use preg_match instead
<?php
$string =array();
$filePath='directorypath/';
$dir = opendir($filePath);
while ($file = readdir($dir)) {
if (preg_match("/.png/",$file) || preg_match("/.jpg/",$file) || preg_match("/.gif/",$file) ) {
$string[] = $file;
}
}
while (sizeof($string) != 0){
$img = array_pop($string);
echo "<img src='$filePath$img' >";
}
?>
Have a look at:
http://php.net/manual/en/function.opendir.php
http://php.net/manual/en/function.scandir.php
To resize images directly from PHP:
http://php.net/manual/en/refs.utilspec.image.php
Here's a one-liner based on another answer to a similar question:
// this will get you full path to images file.
$data = glob("path/to/images/*.{jpg,gif,png,bmp}", GLOB_BRACE);
// this will get you only the filenames
$data= array_map('basename', $data);
Originally, I wanted to use #Imran solution, but mime_content_type wasn't available and the server (on which I have zero control) uses old versions of Apache and Php.
So I changed it a little to work with file extension instead and I'm giving it here.
$imgDir = "images_dir";
// make sure it's a directory
if (file_exists($imgDir)) {
// select the extensions you want to take into account
$image_ext = array(
'gif',
'png',
'jpg',
'jpeg'
);
foreach (scandir($imgDir) as $entry) {
if (! is_dir($entry)) { // no need to weed out '.' and '..'
if (in_array(
strtolower(pathinfo($entry, PATHINFO_EXTENSION)),
$image_ext)) {
// do something with the image file.
}
}
}
}
The code is tested and is working.