Uploading a profile picture and displaying it - php

I want users to be able to upload a profile picture (which can be .jpg or .png) and I also want this to be displayed on their profile. I have written some code, based on sources I found here, on Stackoverflow and Google. However, it does not seem to work and I can't find my mistake.
This is the html
<form action="account_settings.php" method="POST">
<input type="file" name="profilePicture"><br><br>
<input type="submit" value="Change!">
</form>
This is how to uploaded file will be processed.
<?php
include ('inc/header.inc.php');
if(isset($_FILES["profilePicture"]["tmp_name"]) && isset($_FILES["profilePicture"]["name"])) {
$ext = pathinfo($_FILES['profilePicture']['name'], PATHINFO_EXTENSION);
$name = $_SESSION['user_login'];
$tmp_name = $_FILES["profilePicture"]["tmp_name"];
if($ext == 'png' || $ext == 'jpg') {
if (isset($tmp_name)) {
if(!empty($tmp_name)) {
$location = '../profielfotos/';
$full_name = $name.'.'.$ext;
if(move_uploaded_file($tmp_name, $location.$full_name)) {
echo 'Photo uploaded!';
}
Down here are just some else statements with error reports.
The code below is used to display the image. I have tested it by putting an image in the profile pictures folder and it did display the image. However, there is still a problem. People are allowed to upload .jpg or .png, how can I make the website display the picture (find the profile picture with the right extension).
I have put this code inside the src attribute of the <img>tag.
<?php if ($handle = opendir('profielfotos/')) {
$file = mysql_real_escape_string($_GET['u']);
echo 'profielfotos/'.$file.'.png';
}
closedir($handle);
I hope someone can help, thanks in advance!
ps. this is my first post ever on stack overflow :-D!

Since you are not storing any info about the file uploaded, you just have check which file exists, using he file_exists() method. See here:
http://php.net/manual/en/function.file-exists.php
So your code will become something like this (Not tested):
<?php if ($handle = opendir('profielfotos/')) {
$file = mysql_real_escape_string($_GET['u']);
if (file_exists('profielfotos/'.$file.'.png')) {
echo 'profielfotos/'.$file.'.png';
} else if (file_exists('profielfotos/'.$file.'.jpg')) {
echo 'profielfotos/'.$file.'.jpg';
}
}
closedir($handle);

You need to add the following to your form:
<form action="account_settings.php" method="POST" enctype="multipart/form-data">
Otherwise it won't allow a file upload as it expects only text.

This is totally insecure. Files uploaded by a user shall never ever be stored within the root of the web server.
Instead, put the files somewhere outside of the doc root.
Write a handler, which takes control of he files
check the mime type by checking the content, not the extension
have arbitrary names, not the name from the upload, that might interfer (imagine 5 people uploading a "profile.png")
let the handler deliver the image by an id ("...imagloader?file=4711"),
name of the file (and extension and location) is stored in a database (with the user record?)

Related

Are There Any Security Issues In The Following File Upload Script - PHP

I've looked at a number of the answers to StackOverflow questions on safely uploading images with PHP. I've put this following script together with explainers and wanted to know if this is missing anything. My only/main concern is I can't seem to find much info on stripping out harmful code from the image itself, although this is partly covered in the code.
A couple of SO answers touch on the GD image functionality but they don't really give any good code example cases and because I'm new to php I can't quite seem to wrap my head around how to use this (in terms of creating a new version of the image).
Note: This images in this code go to an '/images' directory, but on the live site they will go into a subdomain called 'images' which is outside the public folder and which will serve static files only (no PHP, Perl etc). The short_open_tag will be turned off in the php.ini file.
The files are selected with a file input type with the name 'profile-image'.
The following code is split into its component parts - the first part is the if/isset statements that check that a submit button called 'submit-profile-image' has been clicked, and the files have been uploaded into memory in the ['tmp_name'] key of the $_FILES superglobal:
if(isset($_POST['submit-profile-image'])) {
$allowed = ['jpeg', 'jpg', 'png'];
if($_FILES['profile-image']['error'] === 0 ) {
// THE DIFFERENT CHECKS IN THE MAIN CODE BELOW GO HERE
} else {
$error[] = "Image failed to load please try again";
}
}
This following code all goes inside the 2nd if statement shown above - I've broken it down to show what it is meant to acheive:
Set variable names of temp upload file and file input name
$profileImageName = $_FILES['profile-image']['name'];
$temp = $_FILES['profile-image']['tmp_name'];
Explode string to split the file name and file extension
$ext = explode('.', $profileImageName);
$ext = strtolower(end($ext));
Completely rename file, and only keep the file extension from the original file:
$file = uniqid('', true) . time() . '.' . $ext;
Sanitize string for extra safety (probably not needed)
$file = filter_var($file, FILTER_SANITIZE_STRING);
$file = strtolower($file);
Check the file extention matches the allowed array of file extensions:
if (!in_array($ext, $allowed)) {
$error[] = "File type must be in the jpg / jpeg or png format";
}
Check MIME type using the getImageSize() function which is more reliable than the 'type' key found in the standard $_FILES superglobal:
$getImageSizeMime = getImageSize($temp);
if(isset($getImageSizeMime['mime']) == false) {
$error[] = "Not a recognised MIME type";
} else {
$getImageSizeMime = $getImageSizeMime['mime'];
}
Make sure the MIME type matches the file extension:
if (in_array($ext, $allowed) != $getImageSizeMime) {
$error[] = "Mime type must be of the jpg / jpeg or png format";
}
Inspect contents of image file itself:
$cleanUpload = file_get_contents($temp) ;
Disallow if file contents contain php or script:
if(preg_match('/(<\?php\s)/', $cleanUpload)) {
$error[] = "Image data cannot contain php script tags";
}
if(preg_match('/script/', $cleanUpload)) {
$error[] = "Image data cannot contain javascript tags";
}
Sanitise file contents of HTML tags
$cleanUpload = filter_var($cleanUpload, FILTER_SANITIZE_STRING);
Move uploaded file if none of the above errors are present
if (!isset($error)) {
if(in_array($ext, $allowed)) {
move_uploaded_file($temp, 'images/' . $file);
}
}
Any input on any security issues missed or any extra checks on the image file itself would be hugely appreciated - particularly on how to duplicated the image to only keep image data using the GD library if possible/necessary? Some of the answers on StackOverflow are very old and seem to feature methods that aren't seen as the most up to date either (which I've avoided in this question).
It would be really good to see if there are any PHP methods for checking image files themselves and removing potentially dangerous code.

Permission issues viewing photos uploaded with php

I'm following a tutorial for uploading image files using php on udemy. I can choose an image and upload it to a folder without any problems.
When I click on the image after it has been uploaded to the folder, windows photo viewer says: "photo.png It appears that you don't have permission to view this file. Check the permissions and try again".
When I checked permissions it said "You must have read permissions to view the properties of this file".
I used the chmod function set to 0755, which allows the owner to read and write, and lets everyone else read it. I tried changing the chmod codes but it didn't help.
I'm thinking it has something to do with my server permissions, but can't find any solution on google. My images are uploaded to Abyss Web Server.
Here is the code:
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
function upload_file() {
//setup
$tmp_name = $_FILES['file']['tmp_name'];
$target_dir = 'uploads/';
$target_file = $target_dir . basename($_FILES['file']['name']);
$max_file_size = 5000000; //5mb
$allowed_file_types = array('application/pdf; charset=binary');
$allowed_image_types = array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG);
//check if image type is allowed
$image_check = getimagesize($tmp_name);
if(! in_array($image_check[2], $allowed_image_types)) {
//if not allowed image check if allowed file type
exec('file -bi' . $tmp_name, $file_check);
if(! in_array($file_check[0], $allowed_file_types)) {
return 'This file type is not allowed';
}
}
//check if file already exists
if(file_exists($target_file)) {
return 'Sorry that file already exists';
}
//check file size
if(file_exists($target_file)) {
return 'Sorry this file is too big';
}
//store the file
if(move_uploaded_file($tmp_name, $target_file)) {
chmod($target_file, 0644);
return 'Your file was uploaded';
}
else {
return 'There was a problem storing your file. Try again?';
}
}
if(! empty($_FILES)) {
echo upload_file();
}
?>
<form action="" method="post" enctype="multipart/form-data">
Select image to upload:
<input type="file" name="file">
<input type="submit" Value="upload">
</form>
Since loading the file using a custom made HTML page specifically for testing motives does show the image correctly, then it's most likely a hotlink protection issue. (This was found out after a few comments to the question).
In cPanel, for example, there is a tool to manage this feature and it revolves around the usage of a file called .htaccess. This file is used for a lot of things in the web development world.
Some people don't like their copyrighted images to be accessed, so one way to avoid inexperienced people (let's say, "people in userland") from doing that is to enable this protection. This works for any given file extension that you set it up to.
One way to address this issue is to go to cPanel and disable (or modify accordingly) the Hotlink Protection feature. Another way, is to find the .htaccess file that is causing the issue, which requires understanding the way it works and the syntax it uses.

PHP upload files to remote server

I am completely a novice in all this ...
I have created a Social Networking project in which there is a module which allows user to upload photos..
I have hosted this project in my college server
I access that server using bitvise client with my server credentials.
My problem is i don't know how to setup upload mechanism for remote server ... In my localhost i simply use
move_uploaded_file($_FILES['file']['tmp_name'],$target_file);
function but i don't know how to do this for remote server ...
I tried FTP by looking at some tutorials but that didn't worked for me.
In my project structure there is a directory
users/user_id (diff for all users)/photos
here i want to place the uploaded files....
A proper description with example and proper functioning might be very helpful for me.... Thank you
EDIT:
Below is my code.
Photos.php
<form class="input-group-btn" method="post" action="editPhotos.php"enctype="multipart/form-data" id="myForm">
<input type="file" name="file" id="imgInp">
<button type="submit" class="btn btn-primary" name="form-submit">Done</button>
</form>
editPhotos.php
if( isset($_POST['form-submit']) ){
$target_file = "users/".$email."/pictures/Photos/" . basename($_FILES["file"]["name"]);
$imageFileType = pathinfo($target_file,PATHINFO_EXTENSION);
move_uploaded_file($_FILES['file']['tmp_name'],$target_file);
$img =str_replace(" ", "",basename($_FILES["file"]["name"]));
rename($target_file, "users/".$email."/pictures/Photos/".$img);
header('Refresh: 1; url=Photos.php?user='.$email);
}
Small tutorial how to upload file.
For sure, you need correct encryption and file's type in your form (ommited other fields, to clear example):
form.html
< form action="upload.php" method="post" enctype="multipart/form-data">< /form>
< input name="test" type=file>
upload.php
In $_FILES you have all data of uploaded file. In given example, we have field named test.
Advice, to always first check error $_FILES['test']['error'] - the values you can find in here.
If this is correct, then prepare upload path. Some advices:
remember that if you use original filename ($_FILES['test']['name']), then is User upload second file, with same name, you will need overwrite file or ignore upload. Other way, is to save data to database and generate temporary name form him.
destination path(target_file) - regardless if upload folder is in the same catalog, you should always use global path, as good practice. You can use DIR for that.
don't use in path data, like email - is you have project, and want give opportunity to change email in configuration, what you will do with files? Better save user to Database and use his ID as key.
If you have path, then you simply need only use of move_uploaded_file, but remember to check result, as it not always will return true. You can have error, when you don't have permissions to destination folder (you'll need debug this).
I see that you, first upload file, then rename (then you should check, if rename was success). Don't extends this process, if it not necessary. Upload file for final path and name.
Example of code (I this rattle off)
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
$fileName = basename($_FILES["file"]["name"]);
$fileName = str_replace(" ", "", $fileName);
$target_file = sprintf(__DIR__ . "/users/%s/pictures/Photos/%s", $email, $fileName);
if (move_uploaded_file($_FILES['file']['tmp_name'], $target_file)) {
// File was uploaded
header('Refresh: 1; url=Photos.php?user=' . $email);
} else {
// File was not uploaded;
throw new \Exception('File was not uploaded. Check warnings.');
}
}
Used other method to check, if this is POST
use method sprintf, for better code quality
checked effect of move_uploaded_file
use global path for destination file
Below code is risky in live environment, please use cautiously
Use a relative path to the uploads folder. For example, if your uploads folder is placed outside your current folder. Then
$PATH = '/absolute/example/path/to/uploads';//from config file
$target_file = "$PATH/file_name";
move_uploaded_file($_FILES['file']['tmp_name'],$target_file);
The above code will work both in local and remote server.
Other checks are below:
Check for errors while uploading file in server
To enable error handling use below code in your upload logic:
ini_set('display_errors', 1);
error_reporting(E_ALL);
Another important note is to make uploads folder writable otherwise file upload wont work.

How to view images from protected folder with php?

I have a password protected directory (with .htaccess) on my website containing *.jpg files. I dont want that anyone can directly access these .jpgs - but I want to allow a php script to display the *.jpg files. Is something like that possible?
For those who wonder why I want this:
I have a register form where a user can upload a picture and before finishing the registration he can check if the correct picture was uploaded. For the moment, I save the uploaded picture in a temporary directory and if he finishes it, I move the picture to the password protected directory. However, I dont like that in each registration there is a short time of period where the picture is public (e.g. through a search engine). Even worse, when someone does upload a picture but not complete the registration, then the picture will remain forever in the temp directory, unless I delete somehow. But if I set up a cron-job to delete all images in the temporary directory during a specific time, then it would be possible that someones picture will be deleteted if he registers at a unfavourable moment.
h2ooooooo already answered my question in the comments section.
This is the code how it works, in my code I have to replace
<img src='link/to/protectet/picture.jpg'>
with
<img src='image.php'>
and the image.php consist only of
<?
header('Content-Type: image/jpeg');
readfile('link/to/protectet/picture.jpg');
?>
that worked. Thanks.
I am not sure, whether this is what you want to achieve, but I understand that:
There is a group of picture files that are stored in .htaccess password protected folder and only registered and authenticated users can download files directly in that folder.
For a newly registering user there is a timespan, when a session, that uploaded the image is allowed to download the image, but no other session, whether authenticated or not, is allowed to do so.
In order to do so you could probably:
As you need to distinguish temporary images from valid images: storing the former in /temp folder is actually a good idea, as temporary files will never mix up with valid files.
For every session that is trying to register, you could probably name your uploaded image file using session_id() (i.e. $name = session_id() . '.jpg'). Then a simple script (similar to: php, file download) could provide stored image related to current session. This script can be source address for an <img> tag on registration form.
As for abandoned images in /temp - a cron job could get rid of them indeed. By calling mtime() for each file - you can easily omit files that were created too recently - and so they are probably still in use.
The accepted answer by h2ooooooo is great. But, what prevents someone from typing in the url address for image.php and being served the image? (In fact, this is what I tried, and I was unfortunately able to fetch the image even though it's in a password-protected folder.)
It seems we need a way of determining that the request is coming from a page on the same website, or maybe establishing a session variable prior to this call, and checking its existence before serving the image. There are good suggestions for that here: How to check if a request if coming from the same server or different server?
I ended up doing the following (<img src="getUploadFile.php?fname=my.jpg">):
<?
function requestedByTheSameDomain() {
$myDomain = $_SERVER['SCRIPT_URI'];
$requestsSource = $_SERVER['HTTP_REFERER'];
return parse_url($myDomain, PHP_URL_HOST) === parse_url($requestsSource, PHP_URL_HOST);
}
if(requestedByTheSameDomain()) {
$inputArr = array();
if($_SERVER["REQUEST_METHOD"] == 'POST') {
$inputArr = $_POST;
}
else {
$inputArr = $_GET;
}
$fname = $inputArr['fname'];
$path_info = pathinfo($fname);
$ext = $path_info['extension'];
if (in_array($ext, array('jpg','png','gif','jpeg','bmp','tif','tiff'))) {
$type = 'image';
$subType = $ext;
if($ext == 'jpg') $subType = 'jpeg';
if($ext == 'tif') $subType = 'tiff';
if($ext == 'svg') $subType = 'svg+xml';
}
else if(in_array($ext, array('mpg','ogg'))) {
$type = 'audio';
$subType = $ext;
}
else if($ext == 'mp4'){
$type = 'video';
$subType = $ext;
}
else if($ext == 'pdf') {
$type = 'application';
$subType = $ext;
}
header("Content-Type: $type/$subType");
readfile("images/$fname");
}
?>
All that remains is to disable right-click and/or serve it a background-image to render Save-Image-As difficult.

html page hanging when posting file upload

I'm trying to add a file upload script in php to a website I'm designing. I've used an online example (I know it's not secure and I plan on making it secure, I just want the basic functionality working first).
Basically what's happening is that when I click the "submit" button, the page stalls and says "loading xyz server.." forever and doesn't ever go to the post action php page! This is very frustrating and I can't see why it won't work!
The code is below and I've tried this on 2 different servers with same results. I'd be very grateful if someone could let me know what I'm possibly doing wrong?
<html>
<body>
<form enctype="multipart/form-data" action="do.php" method="post">
<input type="hidden" name="MAX_FILE_SIZE" value="1000000" />
Choose a file to upload: <input name="uploaded_file" type="file" />
<input type="submit" value="Upload" />
</form>
</body>
</html>
<?php
//Сheck that we have a file
if((!empty($_FILES["uploaded_file"])) && ($_FILES['uploaded_file']['error'] == 0)) {
//Check if the file is JPEG image and it's size is less than 350Kb
$filename = basename($_FILES['uploaded_file']['name']);
$ext = substr($filename, strrpos($filename, '.') + 1);
if (($ext == "jpg") && ($_FILES["uploaded_file"]["type"] == "image/jpeg") &&
($_FILES["uploaded_file"]["size"] < 350000)) {
//Determine the path to which we want to save this file
$newname = dirname(__FILE__).'/upload/'.$filename;
//Check if the file with the same name is already exists on the server
if (!file_exists($newname)) {
//Attempt to move the uploaded file to it's new place
if ((move_uploaded_file($_FILES['uploaded_file']['tmp_name'],$newname))) {
echo "It's done! The file has been saved as: ".$newname;
} else {
echo "Error: A problem occurred during file upload!";
}
} else {
echo "Error: File ".$_FILES["uploaded_file"]["name"]." already exists";
}
} else {
echo "Error: Only .jpg images under 350Kb are accepted for upload";
}
} else {
echo "Error: No file uploaded";
}
?>
Thanks very much for your time, I have searched for hours to fix this with no luck!
If you post to an Iframe it won't lock your page. Then watch the Iframe contents to see what the server response is, or better: use Firebug on Firefox and inspect he NET tab to see what's going on with your post request.
How big of a file are you uploading? Most browsers do not display any kind of upload progress bar, and only recently has PHP supported even bare bones progress reports. For a large file, all you'd see is the "... loading ..." status bar text with no indication that data's actually being uploaded.
As for the rest of the code, here's a few things you need to clean up before putting this into production:
$_FILES['uploaded_file']['error'] == 0
This will evaluate to true if the ['error'] element isn't set. Use === instead, which does forces value and type to be equal, not merely value.
if (($ext == "jpg") && ($_FILES["uploaded_file"]["type"] == "image/jpeg") &&
The filename and filetype values in the $_FILES array are what's supplied by the remote user, and can be easily mainipulated. Best to use a server-side mime-type identifer (getimagesize() for pictures works well) to see what the file really is.
Nevermind! It turns out it was something to do with the wireless network at my University. I got home, refreshed the page and it worked fine. I will have to have words with the system administrator about the time I wasted trying to solve a problem made by them.
Thanks for the help anyway guys,
Scott

Categories