Am planning to develop a image upload API which need to upload a image to the server location as part of my project. (The usage will be to upload user pics and avatar using an android app)
The API which should be similar to Imgur API in which we can use a post request to upload a binary image to the server.
I searched through multiple questions, all am getting is using multi part request which requires html form submitting. Since my aim is to create a API, html form submitting will be impossible.
Anyway am submitting a sample code which uses html form to upload an image. Can someone show to how can I modify the script to meet my requirement?
<html>
<body>
<form action="photo.php" method="post"
enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="Submit">
</form>
</body>
</html>
PHP code
<?php
$allow = array("jpg", "jpeg", "gif", "png");
$todir = 'uploads/';
if (!!$_FILES['file']['tmp_name'] ) // is the file uploaded yet?
{
$info = explode('.', strtolower( $_FILES['file']['name']) ); // whats the extension of the file
if ( in_array( end($info), $allow) ) // is this file allowed
{
if ( move_uploaded_file( $_FILES['file']['tmp_name'], $todir . basename($_FILES['file']['name'] ) ) )
{
// the file has been moved correctly
}
}
else
{
// error this file ext is not allowed
}
}
?>
Some remarks about your server-side code in no particular order:
As you can read at Handling file uploads, the correct way to verify an upload is comparing the ['error'] subkey with UPLOAD_ERR_OK (codes explained here). Don't use ['tmp_name'] for that.
Don't let the end user pick the actual file name on your server. You'd better generate a unique name yourself and keep the display name elsewhere (e.g. a database)
The recommended way to determine a file extension is pathinfo:
pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION)
File extension provided by the user is not a reliable way to determine file type. For pictures, the getimagesize function is often used.
multi part request which requires html form submitting
That's wrong. It requires a properly formatted request (headers and body), period. In fact, the server has no way to know what piece of software was used to generate the request—or if you just typed the bytes yourself in a console ;-)
Related
I have an application which uploads data from a csv file and this is working fine. It would be useful, but not essential, if I could limit the dialog window to only show csv files, and if possible a file template say 'abc*.csv'.
The attached image shows an example of a dialog box which will only allow files that start with abc*.csv
Example of csv image dialog box
Thanks
Harry
It depends on how you're handling the uploads.
You can either use plain HTML to filter the .csv extension or handle it using PHP, or both.
Using HTML:
<input type="file" name="upload" accept=".csv">
Using PHP:
$ext = pathinfo($filename, PATHINFO_EXTENSION);
if( $ext !== 'csv' ) {
echo 'Invalid extension.';
}
Note that this only verifies the extension and not the actual filetype.
Also the accept attribute of the <input type="file"> does indeed provider a filter in the file select dialog.
I'm trying to build a simple image uploader using PHP. I do know how to code it, however I have a few concerns..
My question I had is the following: Is saving files which users send safe to save as the original file? With this I mean: Will I not get any vulnerabilities when I'm saving a file send by an user?
Let's say my PHP script does this: It retrieves the POST data, which includes a file send by a person on my website. Is it save to just move the file over to a directory, with original name, content, etcetera? Is there any harm in doing this, or should I rename these files to a random string?
If this isn't a safe way to do this, then what is? How would I verify the send content isn't harmful?
There are always vulnerabilities in storing and providing content provided by a client.
This blog post gives a good description of the vulnerabilities you could face, how they're exploited and how to protect against them. I suggest you use this as a reference: http://blog.insicdesigns.com/2009/01/secure-file-upload-in-php-web-applications/
Make sure you only process files with the correct ending. Image files are never executed by a webserver, but .php files are for example. So make sure the users don't upload any file that can be executed.
I'm not aware of file names that are harmful. For the content this is difficult to answer. I remember an attack vector with modified TIFF images on windows and one in libjpeg on *nix Systems. However you probably won't be able to find these things without completely decoding the image.
Here's another approach: use this (https://hacks.mozilla.org/2011/03/the-shortest-image-uploader-ever/) very short Image Uploader, by Paul Rouget, based on Imgur.com's API.
Instead of uploading directly to your database, let imgur do all the security and validation, then if you need to, link to or download the (safe) image via Imgur.
It's free for non-commercial and very cheap for commercial.
Basically, you can use imgur's API to safely upload images from your HTML page, with no server side code at all
Here's a live demo: (http://paulrouget.com/miniuploader/)
I would say 100% user upload file are NOT 100% SAFE
JPEG files can contain arbitrary data in them in addition to the actual image data; it's part of the spec. Thus, merely checking if an image is a valid JPEG does not mean that the file is necessarily completely harmless.
File upload without validation
HTML Form:
<form enctype="multipart/form-data" action="uploader.php" method="POST"> <input type="hidden" name="MAX_FILE_SIZE" value="100000" /> Choose a file to upload: <input name="uploadedfile" type="file" /><br /> <input type="submit" value="Upload File" /> </form>
PHP Code:
<?php
$target_path = "uploads/";
$target_path = $target_path . basename($_FILES['uploadedfile']['name']);
if (move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
echo "The file " . basename($_FILES['uploadedfile']['name']) . " has been uploaded";
} else {
echo "There was an error uploading the file, please try again!";
}
?>
Define a .htaccess file that will only allow access to files with
allowed extensions.
Do not place the .htaccess file in the same directory where the
uploaded files will be stored. It should be placed in the parent
directory.
A typical .htaccess which allows only gif, jpg, jpeg and png files
should include the following (adapt it for your own need). This will
also prevent double extension attacks.
deny from all
<Files ~ “^w+.(gif|jpe?g|png)$”>
order deny,allow
allow from all
</Files>
If possible, upload the files in a directory outside the server root.
Prevent overwriting of existing files (to prevent the .htaccess
overwrite attack).
Create a list of accepted mime-types (map extensions from these mime types).
Generate a random file name and add the previously generated extension.
Don’t rely on client-side validation only, since it is not enough. Ideally one should have both server-side and client-side validation implemented.
Am cross posting this with the PyroCMS forum in the bid to reach a wider audience for help as I just can't seem to fathom it.
When I try to upload multiple files using the PyroCMS Files library I run into problems. I can seemingly get files to upload singularly when calling the library and I can also get files to upload when bypassing the library and testing via move_uploaded_file. I've created the following (slightly stripped down) code in my controller:
public function upload($id)
{
// Folder selected or redirect
$id or redirect('admin/mis/resources');
// Run on validation
if ($_FILES)
{
if (count($_FILES['userfile']['name'] > 0))
{
foreach ($_FILES['userfile']['name'] as $file)
{
$upload = Files::upload($id, $file);
}
}
redirect('admin/vle/resources/contents/'.$id);
}
$this->template->build('admin/resources/upload');
}
However when running this code, no file is uploaded. If I output the upload I get the following message:
Array ( [status] => [message] =>
You did not select a file to upload.
I seems that I cannot for the life of me work out how to pass more than one file to Files::upload. I sure I'm missing something. However it's not that I cannot get files to upload in general because if I change the code to this locally for testing purposes my files are uploaded just fine:
if($_FILES)
{
if(count($_FILES['userfile']['name']))
{
foreach ($_FILES['userfile']['name'] as $file)
{
$img = "c:/wamp/www/testupload/files/".$file;
move_uploaded_file($_FILES['userfile']['tmp_name'][$i], $img);
}
}
}
The upload method of the files library is here https://github.com/pyrocms/pyrocms/blob/2.2/develop/system/cms/modules/files/libraries/Files.php#L333
I have tried lots of different things to the point where my head is spinning somewhat now trying get to grips with this. So if anyone can help it'd be appreciated.
The Files library doesn't currently support multiple files uploaded by one element. So it is trying to find a single file from the "userfile" file input instead of an array of files.
This should work:
<form method="post" action="/upload/1" enctype="multipart/form-data">
<input name="userfile" type="file"/>
<input name="secondfile" type="file"/>
</form
public function upload($folder_id)
{
$upload = Files::upload($folder_id, 'Descriptive Name', 'userfile');
$second_upload = Files::upload($folder_id, 'Some Name', 'secondfile');
}
The admin panel of PyroCMS uploads multiple files by using the BlueImp jQuery uploader. Perhaps a JS solution like that would work well for you? That way users get progress bars informing them of each upload's status and they can see when each upload completes.
Right now I'm trying to write a script that will only accept certain audio files for upload to a server.
However, some MIME types are being returned as null.
Here is some code fragments:
PHP:
$allowedExt=array('audio/mp4a-latm');
if(isset($_POST))
{
print_r($_FILES);
//ToDo: Limit by File Size
if(in_array($_FILES["fileUpload"]["type"],$allowedExt)){
echo "file type found";
}
else
echo "wrong file type";
}
HTML:
<form action="php/FileUpload.php" method="POST" enctype="multipart/form-data">
Choose a file: <input name="fileUpload" type="file" /><br />
<input type="submit" value="Upload" />
</form>
The result of the above is:
Array ( [fileUpload] => Array ( [name] => 02 Helix Nebula.m4a [type] => [tmp_name] => <removed for space...>))
wrong file type
From what I understand, type should return the file's MIME type. In this case 'audio/mp4a-latm' for a .m4a file.
If php is properly returning null for .m4a files, then what would be the best approach to ensure I'm actually dealing with audio files? Is there anything more definite than just parsing for the file extensions? (ensure someone hasn't change the extension of say a text document)
The MIME element comes from the browser, which means it can be manipulated and thus is not trustworthy.
Either check the extension, or if you really want, parse the first few bytes of the file to make sure it's what is expected.
$_FILES['userfile']['type'] - The mime type of the file, if the browser provided this information.
http://www.php.net/manual/en/features.file-upload.post-method.php
That's why this method doesn't work well. You should compare file extension grabbed from
$_FILES['userfile']['name']
with acceptable extensions array (which you should create by yourself)
If you use php 5.3 or higher you can activate the php file info extension by escaping this line in your php.ini:
extension=php_fileinfo.dll
Then you can get your mime type from the file in php like this:
$pathToFile = 'my/path/to/file.ext';
$fileInfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($fileInfo, $pathToFile);
finfo_close($fileInfo);
This is more reliable than using what the browser sends you in the $_FILES array from your POST request.
I'm coding a form using PHP and jQuery. I have a file upload field to which I applied the following jQuery-validate rules:
image: {
accept: "jpeg|jpg|png|gif",
required: "#edit_ID:blank"
}
The file upload field must only accept jpeg/jpg, png, or gif images. It is required only if the field with the ID "edit_ID" is left blank.
This is my file upload field:
<input type="file" id="image" name="image" class="input1" />
The file upload field selects files without any problem, but when jQuery returns an error because of a violated rule, even if I select a new file from my computer the file I selected before the error appeared remains there. It's as if it becomes disabled after jQuery-validate gives an error.
I tried removing the rules set for this field, tested it, and encountered no problem.
Does anybody have an idea what's causing this problem?
Thanks.
P.S.: I encounter the problem in Firefox (v.3.6.9) and IE (v.8) but not in Google Chrome (v.5).
This will not answer your original question, technically.
But, it's not a good idea to trust client side programming to do image validation; your program can be fooled. Since you are using PHP anyway, why not use PHP to do some server side validation?
All files uploaded are stored in the $_FILES array. You can check file type like so:
<?php
foreach($_FILES as $file)
{
if(!empty($file['type'])) //only check if there's a valid file type
{
if($file['type'] != 'image/jpeg' && $file['type'] != 'image/png' && $file['type'] != 'image/gif')
{
//this file is not any one of the accepted formats, so long!
}
else{//do your processing}
}
else{//error invalid file}
}
?>
In case you want instant validations, you can still use AJAX, instead of relying solely on the client side to do the validation.