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.
Related
I'm trying to debug this issue by posting raw PNG image data to the server with the help of Postman. Here's a screenshot, which might help to understand the issue:
On the server I'm receiving the file as follows:
$png = $GLOBALS["HTTP_RAW_POST_DATA"];
Then I write the data to a new file:
$fh = fopen($myFile, 'w') or die("can't open file");
fwrite($fh, $png);
fclose($fh);
The file gets saved correctly, but it now has a different file size,
417KB instead of 279KB which is the size of the original file.
Now of course, I can't do any image operation as none of the functions (such as getimagesize which returns bool(false)) recognizes the file as a valid image.
I have debugged this process to a point where the issue must be somewhere in the file operations, but I don't understand why the file just doesn't result in the very same file type and size as the original, when the only thing I am doing is using the same raw data.
UPDATE:
I've now compared the encodings of the original file with the uploaded one,
and the former is in ISO-8859-1 and it displays correctly, the latter is in UTF-8 and has about 138kB more in file size.
Now I've achieved to convert the file on the server to ISO-8859-1.
fwrite($fh, iconv("UTF-8", "ISO-8859-1", $png));
The resulting file does now have the same output file size (279kB),
but it is still not recognized as a PNG image, some information seems to still get lost.
UPDATE (1):
I've been able to examine the issue further and found out, that the original file is exactly 4 bytes bigger than the generated file, thus the resulting PNG seems to be corrupted.
UPDATE (2):
I'm now able to save the file and open it as a valid PNG. The following code seems to be saving the image correctly:
$input = fopen("php://input","r+");
$destination = fopen($myFile, 'w+');
stream_copy_to_stream($input, $destination);
fclose($input);
fclose($destination);
However when trying to open the file with the imagecreatefrompng function I get a 500 error. I'm now trying to figure out if it's a memory issue in PHP.
Problem might be the way you test your POST by copying the "binary" data into a text field.
If you paste the same data into a text editor you won't get a valid image file either when saving this with the png extension.
Try to build a simple form with file field to test your upload
I use nginx for uploads and haven't had a problem, but I use the standard PHP way of uploading files as per: http://www.php.net/manual/en/features.file-upload.post-method.php
I would suggest trying that.
Try using: < ?php $postdata = file_get_contents("php://input"); ?>
To get the raw data. I use it some times to get data sent from a ajax post on cake.
I have moved my images to Rackspace Cloud Files and am using their PHP API. I am trying to do the following:
Get an image from my "originals" container
Resize it, sharpen it, etc.
Save the resized image to the "thumbs" container
My problem is with #2. I was hoping to resize without having to copy the original to my server first (since the images are large and I'd like to resize dynamically), but can't figure out how. This is what I have so far (not much):
$container = $conn->get_container("originals");
$obj = $container->get_object("example.jpg");
$img = $obj->read();
Part of the problem is I don't fully understand what is being returned by the read() function. I know $img contains the object's "data" (which I was able to print out as gibberish), but it is neither a file nor a url nor an image resource, so I don't know how to deal with it. Is it possible to convert $img into an image resource somehow? I tried imagecreatefromjpeg($img) but that didn't work.
Thanks!
First, you cannot resize an image without loading it into memory. Unless the remote server offers some "resize my image for me, here are the parameters" API, you have to load the image in your script to manipulate it. So you'll have to copy the file from the CloudFiles container to your server, manipulate it, then send it back into storage.
The data you receive from $obj->read() is the image data. That is the file. It doesn't have a file name and it's not saved on the hard disk, but it is the entire file. To load this into gd to manipulate it, you can use imagecreatefromstring. That's analogous to using, for example, imagecreatefrompng, only that imagecreatefrompng wants to read a file from the file system by itself, while imagecreatefromstring just accepts the data that you have already loaded into memory.
You can try to dump the content of the $img variable into a writable file as per the below:
<?php
$filename = 'modifiedImage.jpg';
/*
* 'w+' Open for reading and writing; place the file pointer at the beginning of the file and truncate
* the file to zero length. If the file does not exist, attempt to create it.
*/
$handle = fopen($filename, 'w+');
// Write $img to the opened\created file.
if (fwrite($handle, $img) === FALSE) {
echo "Cannot write to file ($filename)";
exit;
}
echo "Success, wrote to file ($filename)";
fclose($handle);
?>
More details:
http://www.php.net/manual/en/function.fopen.php
http://www.php.net/manual/en/function.fwrite.php
Edit:
You might also want to double check the type of data returned by the read() function, because if the data is not a jpg image, if it's for example a png, the extension of the file needs to be changed accordingly.
I am using plupload to do an upload of multiple files to my server. Using this, there is a parameter 'url : 'upload.php'. upload.php catches the files as they are received, and might recombine them if they get chunked. Once the full file is received, it sends a response back to the original page, displaying a green checkbox icon.
I have added some code to this page, after all the main code to manipulate the photos I have uploaded. My plan is to create three copies of my full size image, lg, med, and small. I got this part working, but then decided to first rename the original file to match my naming scheme.
I now get a corrupted renamed file, and thus my three smaller images also get corrupted.
//get the original file info
$filepath = $_SERVER['DOCUMENT_ROOT'].'/uploads/';
$filepathinfo = pathinfo($filepath.$fileName);//fileName is used previously in the file
//rename original file to a unique name
$finding_id = 'xyz';
$file_name_new = uniqid($client_id . '-' . $finding_id . '-', true); //doesn't include extension
//rename($filepath.$fileName, $filepath.$file_name_new.'.'.$ext);
//copy($filepath.$fileName, $filepath.$file_name_new.'.'.$ext);
As is, I get my one file, or how ever many I uploaded, byte size matches original exactly, and name stays the same (except for removal of certain characters).
If I uncomment only the rename function, I actually get two files. The byte sizes total the original photo. The larger file displays with a section of gray at the bottom. The smaller file doesn't display at all.
If I uncomment only the copy function, I get an exact renamed copy of my original file, my original file, and another file, the same size and corruption as the larger file doing a rename.
Any ideas? Seems like it should be pretty straightforward.
if the file was currently uploaded by HTTP POST use move_uploaded_file
if you fopen() somewhere in this request the same file make sure to call fclose()
I forgot I had the chunking feature turned on. Must have turned it on to test something. For whatever reason, when the script was running the last chunk of the file hadn't been fully appended yet. Thanks for all the input anyway!
Are you writing to the file yourself? If so, the problem might be that you're missing a call to fflush or fclose. (The last chunk of the file not getting written and the file no longer being there when PHP gets round to writing it. This shouldn't happen if you're using Linux or some other Unix, but I could envisage it on Windows.)
I'm implementing a user-based image uploading tool for my website. The system should allow any users to upload JPEG and PNG files only. I'm, of course, worried about security and so I'm wondering how the many smarter people than myself feel about the following checks for allowing uploads:
1) First white list the allowable file extensions in PHP to allow only PNG, png, jpg, JPG and JPEG. Retrieve the user's file's extension via a function such as:
return end(explode(".", $filename));
This should help disallow the user from uploading something malicious like .png.php. If this passes, move to step 2.
2) Run the php function getimageize() on the TMP file. Via something like:
getimagesize($_FILES['userfile']['tmp_name']);
If this does not return false, proceed.
3) Ensure a .htaccess file is placed within the uploads directory so that any files within this directory cannot parse PHP files:
php_admin_value engine Off
4) Rename the user's file to something pre-determined. I.E.
$filename = 'some_pre_determined_unique_value' . $the_file_extension;
This will also help prevent SQL injection as the filename will be the only user-determined variable in any queries used.
If I perform the above, how vulnerable for attack am I still? Before accepting a file I should hopefully have 1) only allowed jpgs and pngs, 2) Verified that PHP says it's a valid image, 3) disabled the directory the images are in from executing .php files and 4) renamed the users file to something unique.
Thanks,
Regarding file names, random names are definitely a good idea and take away a lot of headaches.
If you want to make totally sure the content is clean, consider using GD or ImageMagick to copy the incoming image 1:1 into a new, empty one.
That will slightly diminish image quality because content gets compressed twice, but it will remove any EXIF information present in the original image. Users are often not even aware how much info gets put into the Metadata section of JPG files! Camera info, position, times, software used... It's good policy for sites that host images to remove that info for the user.
Also, copying the image will probably get rid of most exploits that use faulty image data to cause overflows in the viewer software, and inject malicious code. Such manipulated images will probably simply turn out unreadable for GD.
Regarding your number 2), don't just check for FALSE. getimagesize will also return the mime type of the image. This is by far a more secure way to check proper image type than looking at the mime type the client supplies:
$info = getimagesize($_FILES['userfile']['tmp_name']);
if ($info === FALSE) {
die("Couldn't read image");
}
if (($info[2] !== IMAGETYPE_PNG) && ($info[2] !== IMAGETYPE_JPEG)) {
die("Not a JPEG or PNG");
}
All the checks seem good, number 3 in particular. If performance is not an issue, or you are doing this in the background, you could try accessing the image using GD and seeing if it is indeed an image and not just a bunch of crap that someone is trying to fill your server with.
Concerning No. 2, I read on php.net (documentation of the function getimagesize() ):
Do not use getimagesize() to check that a given file is a valid image. Use a purpose-built solution such as the Fileinfo extension instead.
For an image file (JPEG) that has been uploaded to the server via a PHP script (to a directory such as http://www.somedomain.com/images, is it a good idea to allow the client to get the image's direct address (such as http://www.somedomain.com/images/someimage.jpg and paste it into a WYWSIWYG text editor (such as TinyMCE)?
I am wondering if there is a preferable method where the direct address is encrypted?
Please, if I should just link directly to the image, just say so.
Thanks!
Note: I have modified this question from my original. Please see revisions if you are curious, but I think I was asking the question incorrectly. My apologies to the people who already answered.
As long as you check correctly WHAT is being uploaded, it shouldn't be a problem. So please at least use getimagesize or a similar function to make sure it's an image that's being uploaded, AND make sure the extension on the file is correct so that it will never be run through the PHP interpreter - to prevent someone from uploading an image with a PHP script attached.
BTW Here's a nice whitepaper on uploads and security : http://www.scanit.be/uploads/php-file-upload.pdf
Depending on the CPU Constraints of your web-hosting service you can write a service to 'serve' the images to your users.
Here is some very BASIC code, it needs spiffing up and cleaning up for XSS/etc...
<?php
$basePath = "/path/to/my/image/store/not/web/accessible/";
$file = NULL;
if (isset($_GET['file']))
$file = $_GET['file'];
if ($file != NULL)
{
$path = $basePath . $file;
// $file needs to be checked for people
// trying to hack you, but for the sake of simplicity
// i've left it out
$mime = mime_content_type($path);
$size = filesize($path);
header("Content-Length: " . $size);
header("Content-Type: " . $mime);
header('Expires: 0');
readfile($path); // Outputs the file to the output buffer
}
?>
Obviously you can put whatever security checks in here you want. But this way your files are below the web dir, and you can apply logic to thier accesibility. This is typically used more for FILE vs. Images, but you can do the same thing here.
Images Accessed like this
http://www.mysite.com/image.php?file=hello.jpg
And you can use mod_rewrite to rewrite urls like this:
`http://www.mysite.com/images/hello.jpg
Into the first url.
I Cannot stress enough the need for further security checking in the above example, it was intended to show you how to serve a file to the user using PHP. Please don't copy & use this verbatim.
Wordpress uses direct links for images. The permalink function simply puts the image on a page along with metadata for comments, but the images' SRC attributes still link directly to the image.
why are you concerned about revealing your image location. Hotlinking?
if so you can prevent hotlinking with htaccess
http://altlab.com/htaccess_tutorial.html
Didn't you get your answer already?
Every site reveals image location to the browser. It's just the way web works.
Got any reason to "encrypt" original location?