Following this thread: PHP file upload: mime or extension based verification?
I assume that I need to check the file extension of the file that I am uploading, correct ?
I am trying to upload a binary file that results from a make file into a Raspberry using a PHP Interface.
This is the file in question:
Big_ppd_display_try1: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=047e67dcea785cb3139bc690aebcf0d537ef40fe, with debug_info, not stripped
Following this thread: php check file extension in upload form
I can try:
$allowed = array('gif','png' ,'jpg');
$filename = $_FILES['uploaded_file']['name'];
$ext = pathinfo($filename, PATHINFO_EXTENSION);
if(!in_array($ext,$allowed) ) {
echo 'error';
}
But how do I tell PHP to only allow binary files like Big_ppd_display_try1 that have no file extension ?
Also, I am doing the upload from a Linux machine. How will that binary file look like on a Windows PC ?
To make sure file has no extension, compare it with null.
To check for mime tipe use finfo_ functions:
$filename = $_FILES['uploaded_file']['name'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['uploaded_file']['tmp_name']);
$ext = pathinfo($filename, PATHINFO_EXTENSION);
if ($ext === null && $mime === 'application/octet-stream') {
//do something
}
We can use file command if you are using linux like as bellow
$command = "file $_FILES['uploaded_file']['tmp_name']";
shell_exec($command);
It will return a string like
ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=047e67dcea785cb3139bc690aebcf0d537ef40fe, with debug_info, not stripped
You can evaluate the data returning the file type you want with this string.
I assume that I need to check the file extension of the file that I am uploading, correct ?
No.
The file extension is an arbitrary part of the "suggested filename" which is entirely under the control of the user. The procedure you should be following is:
Inspect the contents of the file as shown in this question.
Reject the file if the detected type is not in your allowed list.
Generate your own filename to save it to:
the extension should be based on the file type determined at step 1
the rest of the name might be based on what the user suggested, but should be filtered through a whitelist of allowed characters, e.g. replacing everything other than letters and numbers with -
I don't know if this is the best resolve but I have ended up checking if the file is an application/octet-stream:
<?php
if (isset($_POST['update_button']) && $_POST['update_button'] == 'Update') {
if (isset($_FILES['uploaded_file']) &&
$_FILES['uploaded_file']['error'] === UPLOAD_ERR_OK &&
$_FILES['uploaded_file']['type'] == "application/octet-stream")
{
// print_r($_FILES);
echo "<br>Successful upload !<br> ";
} else {
echo "<br>File was not uploaded !<br> ";
}
}
?>
I have ditched checking for file extension or MIME type because I think these can be easily bypassed.
I am now trying to execute the file with a certain argument and check it's response.
This is the code I am working on now:
$fileTmpPath = $_FILES['uploaded_file']['tmp_name'];
$fileName = $_FILES['uploaded_file']['name'];
// echo "<br>$fileTmpPath"."/$fileName<br>";
$command = "sudo .$fileTmpPath"."/$fileName -argument";
echo "<br>$command<br>";
$response = exec($command, $full, $status);
if($status != 0) {
echo "<br>Something went wrong<br>";
} else {
echo "<br>$response<br>";
}
Related
I'm facing an issue with file uploads using Yii2 framework, but I think that question goes deeper than a framework problem. I have an app that allow the user do pdf files uploads, until here my app works fine but I'm in trouble when some smartass rename the filename extension from anything to pdf. The app isn't validating this kind of trick.
I tried without success to validate the mimetype. Now I'm looking for another way.
Anyone know how to block this kind of cheat?
Its better to keep it simple and just use this
<?php
$finfo = finfo_open(FILEINFO_MIME_TYPE);
if(finfo_file($finfo,$filename) == 'application/pdf'){
// input file is pdf
}
?>
Since you said its not working for you you can try these
if you are using a Linux server you can use the shell commands to check them mime type
<?php
function detectMimeType($filename='')
{
$filename = escapeshellcmd($filename);
$command = "file -b --mime-type -m /usr/share/misc/magic {$filename}";
$mimeType = shell_exec($command);
return trim($mimeType);
}
?>
Or you can try this method .Here we assume that Pdf file starts with a %PDF string .[usually it does start with %PDF].
<?php
function detectFileType($filename='')
{
$handle = fopen($filename, "rb");
$contents = fread($handle, 4);
fclose($handle);
if($contents == "%PDF")
{
return "application/pdf";
}
else
{
return "application/octet-stream"; //unknown type
}
}
?>
[this code is not tested ]
Refer these links you will get some more info about what went wrong
http://php.net/manual/en/function.mime-content-type.php
http://php.net/manual/en/ref.fileinfo.php
the best way is to check mime type of file :
http://php.net/manual/en/function.finfo-file.php
<?php
$finfo = finfo_open(FILEINFO_MIME_TYPE);
if(finfo_file($finfo,$filename) == 'application/pdf'){
// input file is pdf
}
finfo_close($finfo);
?>
The problem was solved using the mime_content_type function.
Check the function here php.net
This function returns the real mime type.
I'm uploading images from my Android app to my server. The app uses the android camera intent and upload via PHP script is ok.
I want to verify if the uploaded files are real images, I'm not checking the extension but the mimetype (I suppose this is the best way to do it, tell me if I'm wrong).
I'm using a Slackware Linux Apache server and I'm trying this code:
....
$finfo = finfo_open(FILEINFO_MIME, '/etc/httpd/magic');
....
fwrite($fp, finfo_file($finfo, "file.jpg"));
....
But I'm getting "application/octet-stream; charset=binary" instead of "image/jpeg; charset=binary" which is given from "file -i file.jpg" (shell command).
What's the problem?
Solved using $finfo = finfo_open(FILEINFO_MIME); instead of the other line. I think the default magic file is not the same that I was specifing.
As refered on www.php.net/manual/en/ref.fileinfo.php:
<?php
function is_jpg($fullpathtoimage){
if(file_exists($fullpathtoimage)){
exec("/usr/bin/identify -format %m $fullpathtoimage",$out);
//using system() echos STDOUT automatically
if(!empty($out)){
//identify returns an empty result to php
//if the file is not an image
if($out == 'JPEG'){
return true;
}
}
}
return false;
}
?>
Alternately, if you've got execution rights and want to use a "hacky" solution, you can simply do what you've already done (using file -i path with shell_exec):
<?php
function shell_get_mime_type($path) {
if (is_readable($path)) {
$command = 'file -i "' . realpath($path) . '"';
$shellOutput = trim(shell_exec($command));
//eg. "nav_item.png: image/png; charset=binary"
if (!empty($shellOutput)) {
$colonPosition = strpos($shellOutput, ':');
if ($colonPosition !== false) {
return rtrim(substr($shellOutput, $colonPosition + 1));
}
return $shellOutput;
}
}
return false;
}
?>
Try to use function mime_content_type().
I am writing a script and I need to correctly (I think some mime types can be different from their extensions) get the mime types of files (the files can be of any type).
Web hosting company in use does not have mime_content_type() and is still (don't know which year they will be fixing it) promising to fix the PECL alternative.
Which other way can I go about it (and I don;t have access to shell commands)?
You could try finfo_file - it returns the mime type.
http://php.net/manual/en/book.fileinfo.php
I use the following function, which is a wrapper for the 3 most common methods:
function Mime($path, $magic = null)
{
$path = realpath($path);
if ($path !== false)
{
if (function_exists('finfo_open') === true)
{
$finfo = finfo_open(FILEINFO_MIME_TYPE, $magic);
if (is_resource($finfo) === true)
{
$result = finfo_file($finfo, $path);
}
finfo_close($finfo);
}
else if (function_exists('mime_content_type') === true)
{
$result = mime_content_type($path);
}
else if (function_exists('exif_imagetype') === true)
{
$result = image_type_to_mime_type(exif_imagetype($path));
}
return preg_replace('~^(.+);.+$~', '$1', $result);
}
return false;
}
$_FILES['file']['type'] comes from the browser that uploads the file so you can't rely on this value at all.
Check out finfo_file for identifying file types based on file content. The extension of the file is also unreliable as the user could upload malicious code with an mp3 extension.
I need to check whether a given image is a JPEG.
if ($_FILES["fname"]["error"] > 0) {
$imgData = "hyperlink/holder.jpg";
} else {
$imgData ="hyperlink/" . $_FILES["fname"]["name"];
}
// Only accept jpg images
// pjpeg is for Internet Explorer should be jpeg
if (!($_FILES["fname"]["type"] == "image/pjpeg") ) {
print "I only accept jpg files!";
exit(0);
}
When it goes to first statement in the first if statement it always gives I only accept jpg files!
How can I fix it?
Try the exif_imagetype image function.
Example:
if(exif_imagetype($filepath) != IMAGETYPE_JPEG){
echo 'Not a JPEG image';
}
PHP has such good image-type support, i wonder why you are restricting your app. In just a couple lines of code you can deal with any input format and convert to jpeg, if that is a requirement...
$im = imagecreatefrompng(input_filename)
imagejpeg($im, output_filename);
I believe the following works:
Also note that:
(exif_imagetype($ImagePathAndName) == IMAGETYPE_JPEG)
only reads the first few bytes looking for an image header so isn't really good enough to confirm if an image is corrupt.
Below I have it in a logical “and” statement i.e. both of these tests must be passed in order for the image to qualify as being valid and non-corrupt etc:
if ((exif_imagetype($ImagePathAndName) == IMAGETYPE_JPEG) && (imagecreatefromjpeg( $ImagePathAndName ) !== false ))
{
echo 'The picture is a valid jpg<br>';
}
Note: You need to place this line of code at the top of the php code in order to avoid seeing the warning messages from imagecreatefromjpeg( $ImagePathAndName ) when it encounters a fake/corrupt image file.
ini_set(‘gd.jpeg_ignore_warning’, 1);
Why don't you try creating an array of exceptions (the files you want the user to be able to upload).
// Hyperlink for your website
$hyperlink = "http://www.yourwebsitehere.com";
if($_FILES['fname']['error'] > 0)
{
$image= $hyperlink . "/holder.jpg";
}
else
{
$image = $hyperlink . "/" . $_FILES['fname']['name'];
}
// Only accept files of jpeg format
$exceptions = array("image/jpg", "image/jpeg", "image/pjpeg");
foreach($exceptions as $value)
{
if($_FILES['fname']['type'] != $value)
{
echo "I only accept jpeg images!";
break; // Or exit();
}
}
When using $_FILES, you are relying on informations sent by the client, which is not the best thing to do (you've seen it's not always the same, and, if I remember correctly, $_FILES['...']['type'] can be faked).
If you are using PHP >= 5.3 (or can install PECL packages), maybe you can give a look to the extension Fileinfo. If you are using an older version, what about mime_content_type?
And, as said by Scott, why allow only jpeg?
Looking about the code better : when you are in the first case (error > 0), you are assigning a default file to $imgData? Why the spaces around "hyperlink"?
And why do you always use to check the content-type, even if there was an error a couple of lines before?
To finish, did you have a look at the manual (Handling file uploads)?
Check the mime (Multipurpose Internet Mail Extensions) type of file with this code. And verify your desired type. You can also detect png,gif with this code.
if($_FILES["fname"]["type"] == "image/jpeg")
{
echo "File type is JPEG";
}
On the PHP website, the only real checking they suggest is using is_uploaded_file() or move_uploaded_file(), here. Of course you usually don't want user's uploading any type of file, for a variety of reasons.
Because of this, I have often used some "strict" mime type checking. Of course this is very flawed because often mime types are wrong and users can't upload their file. It is also very easy to fake and/or change. And along with all of that, each browser and OS deals with them differently.
Another method is to check the extension, which of course is even easier to change than mime type.
If you only want images, using something like getimagesize() will work.
What about other types of files? PDFs, Word documents or Excel files? Or even text only files?
Edit: If you don't have mime_content_type or Fileinfo and system("file -bi $uploadedfile") gives you the wrong file type, what other options are there?
Take a look at mime_content_type or Fileinfo. These are built-in PHP commands for determining the type of a file by looking at the contents of the file. Also check the comments on the above two pages, there are some other good suggestions.
Personally I've had good luck using something that's essentially system("file -bi $uploadedfile"), but I'm not sure if that's the best method.
IMHO, all MIME-type checking methods are useless.
Say you've got which should have MIME-type application/pdf. Standard methods are trying to find something that looks like a PDF header (%PDF- or smth. like that) and they will return 'Okay, seems like this is a PDF file' on success. But in fact this doesn't means anything. You can upload a file containing only %PDF-1.4 and it will pass MIME-check.
I mean if the file has an expected MIME-type - it will always pass the MIME-type check otherwise the result is undefined.
I assume you are going to have a fixed white-list of file-types that you will accept.
For each of these types, you are going to have to use different techniques to verify that they are valid examples of that format.
There are two related questions:
Does it look roughly like it might be the right type? (For JPEG, you could check the headers, as you mentioned. For many Unix-based formats, you could check the "magic cookie".)
Is it actually a valid example of that type (e.g. For any XML-like format, you could validate against a DTD.)
I think that, for each format, you should ask separate questions for each one, because the answer will be quite different for PDFs compared to ZIP files.
I used mime_content_type that is compatible with PHP 5.2, because I can use neither Fileinfo (it requires PHP 5.3) nor system(), that is disabled by my provider.
For example, I check if a file is a text file so:
if (strcmp(substr(mime_content_type($f),0,4),"text")==0) { ... }
You can see a full example in my "PHP Directory and Subdirectory Listener & File Viewer and Downloader" at:
http://www.galgani.it/software_repository/index.php
if(isset($_FILES['uploaded'])) {
$temp = explode(".", $_FILES["uploaded"]["name"]);
$allowedExts = array("txt","htm","html","php","css","js","json","xml","swf","flv","pdf","psd","ai","eps","eps","ps","doc","rtf","ppt","odt","ods");
$extension = end($temp);
if( in_array($extension, $allowedExts)) {
//code....
} else {
echo "Error,not Documentum type...";
}
}
Here is the function file_mime_type from iZend:
function file_mime_type($file, $encoding=true) {
$mime=false;
if (function_exists('finfo_file')) {
$finfo = finfo_open(FILEINFO_MIME);
$mime = finfo_file($finfo, $file);
finfo_close($finfo);
}
else if (substr(PHP_OS, 0, 3) == 'WIN') {
$mime = mime_content_type($file);
}
else {
$file = escapeshellarg($file);
$cmd = "file -iL $file";
exec($cmd, $output, $r);
if ($r == 0) {
$mime = substr($output[0], strpos($output[0], ': ')+2);
}
}
if (!$mime) {
return false;
}
if ($encoding) {
return $mime;
}
return substr($mime, 0, strpos($mime, '; '));
}
For PHP>=5.3.0, you can use php's finfo_file(finfo_file) 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.
So just make it in one function,
var_dump(mime_type("wiki templete.txt")); // print string(10) "text/plain"
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;
}
MimeTypes