I store uploaded pictures in a folder while I store the path and filename in a database. Please how best can I generate unique filenames to avoid name conflict in the folder? Thanks
You've tagged your question with PHP and MySQL.
You could use PHP's uniqid function to generate a unique string
You could use a MySQL table to store information about the images, and use an AUTO_INCREMENT key in that table to give you a unique integer which you could use as the file name
Do you have any more specific requirements/goals? If not, I imagine either of those solutions should work for you.
Probably the closest unique value you have is the database file ID. But if you save your files in different tables this will no longer be the case (ex: ArticleFiles and NewsFiles tables). In this case you can use separate directories for separate tables, or prefixing your files.
You may also be required to upload files to the same server location from different host names. In this case you can prefix the file name with the host name used for upload.
People tend o use the time() function, but that's mostly informative, as it doesn't protect you against concurrent uploads. It does help you in finding the files on the disk easier, by ordering. For the same reason you may include the original extension in the file name.
In the end, use the shortest and most informative unique value or composed value you can get, like
$file = "{$HOST}.{$TYPE}.{$TIMESTAMP}.{$DATAID}.{$EXT}.uploaded";
However, be very careful with access rights on your uploads folder, as it's a serious security risk. Do not allow execution rights, try placing it outside of the document root, check uploads for possible dangerous files, etc.
You can use
$image_name=('Your folder path/'. time () .'.'$extension);
for get any image extension
$extension = getExtension($filename);
function getExtension($str) {
$i = strrpos($str,".");
if (!$i) { return ""; }
$l = strlen($str) - $i;
$ext = substr($str,$i+1,$l);
return $ext;
}
is little complex but its make your work easy , you can use this extension for validation on your file type
like
if (($extension != "jpg") && ($extension != "jpeg") && ($extension != "png") && ($extension != "gif"))
{
echo '<h1>Unknown extension!</h1>';
$errors=1;
}else
//your code to save image in folder.
Have you considered storing the data in the database ?
If you are able to; you will get rid of the need to have unique file name, and the problems associated with the files & database being out of synch.
Related
I'm developing a system where the user will be able to upload a ".docx" file. Is verifying it's extension enough to know that this ".docx" file isn't infected?
Here's my upload PHP code:
<?php
session_start();
include("connection.php");
include("functions.php");
// Just to validate the user
$user_data = check_login($con);
include("connectionPostsDB.php");
if (isset($_POST['submit'])){
$title = $_POST['title'];
$tag = $_POST['tag'];
$description = $_POST['description'];
$file = $_FILES['file'];
$fileName = $_FILES['file']['name'];
$fileTmpName = $_FILES['file']['tmp_name'];
$fileSize = $_FILES['file']['size'];
$fileError = $_FILES['file']['error'];
$fileType = $_FILES['file']['type'];
$fileExt = explode('.', $fileName);
$fileActualExt = strtolower(end($fileExt)); //here I get the actual file's extension (I hope xD)
$allowed = array('docx');
if(in_array($fileActualExt, $allowed)){
if($fileError === 0){
if($fileSize < 1000000){
$fileNameNew = uniqid('', true).".".$fileActualExt;
$fileDestination = '../imgs/posts/'.$fileNameNew;
move_uploaded_file($fileTmpName, $fileDestination);
$query = "INSERT INTO posts (title, descr, imgname, tag)
VALUES ('".$title."','".$description."','".$fileNameNew."','".$tag."')";
mysqli_query($postcon, $query);
echo 'File successfully uploaded';
}
else {
echo 'Your file is too big.';
}
}
else {
echo 'There was an error uploading your file.';
}
}
else {
echo 'This type of file not allowed.';
}
}
So, me checking for the file's extension is enough to prevent some user to put some php code in my server (or do something to get information from the server)?
No, just checking the extension isn't enough, however the main question is what you exactly mean by safe.
The code you've shown only stores the file on your server (under a server-generated name), and then publishes its existence in the database. Neither of those things themselves would be dangerous, even if the file contained the digital equivalent of the black plague.
But let's first take a step back; your SQL query is very much unsafe, though that has nothing to do with the uploaded files. You should read up on prepared statements as your entire site is wide open to SQL injection when you create queries this way.
Now, back on topic:
There are at least 2 things you could explore in order to become more relaxed about the .docx file being what it should be.
As far as i know, the docx format is really just a .zip file with some .xml files inside. PHP understands both those formats, so you could attempt to unzip it's contents, see if it contains the sorts of files you expect, including xml(s), and then see if those XML files are parse-able.
You could feed the uploaded file to a virusscanner
Neither of these methods is full proof, but they will both illuminate the situation a bit, allowing you (more specifically, your algorithm) to make a more informed decision.
Now as for what i ment with "what you exactly mean by safe". The actions you take here (merely renaming and storing the file and writing text to a database) are fundamentally pretty safe.
But i imagine there is someone or something else, on the other side of that database, that reads the same DB table, and allows someone to either open and or download that file. Now that action does actually have a lot of security implications.
Now if that database is only viewed through your application, then you could theoretically waive all responsibility and say "this file was anonymously uploaded by god knows who, treat it as if it arrived in your emails junk folder by unknown sender". But if you're taking any level of responsibility (be it implied or literal), you'll have to verify it to whatever degree you're comfortable with.
Nope, it's never enough just chekcing the file extension because we can set .docx extension to any file type.
We should also check many more things like:
Is the mime type of the file expected?
Is the file size reasonable enough. For example, if someone upload a 25 GB docx file, then even if it's a valid docx file your server might go down.
Ideally you shouldn't upload any file in any production server instead you should upload in any storage provider like s3 bucket of AWS. So that even if that file is executable it's not being executed in your server. It's uploaded into a different server that is well optimized to handle files.
And many more such things to consider.
I’m having a file upload system in my website for a long time. During the time, I realized that many users upload files that contain same names but are different from each other. As the names are same, the upload fails!
I’ve decided not to use the $_FILES['file']['name'] for the filenames again.
In my case, a random echo of a $_FILES['file']['tmp_name'] looks like /tmp/phpynIF3G. I’m going to use the $_FILES['file']['tmp_name'] for the file name.
For example, at first it was $filename = $_FILES['file']['name']; and now it’d be $filename = str_replace("/tmp/php","",$_FILES['file']['tmp_name']).".jpg";.
So, when the file will be successfully uploaded to my /e/ directory, it can be accessed by a url which is https://sitename/e/ynIF3G.jpg.
If Php has already used the /tmp/phpynIF3G once, will it ever use the same tmp_name for an other file again?
If each user uploads a file named XYZ.jpg, and if you want them to not be overwriten, you have to either validate if the file exists and change its name or separate where to save the files.
Usually, theres a file directory that can you structure your files, something like user/userid/files/XYZ.jpg.
$_FILES['file']['tmp_name'] is a name, temporary for that matter, randomly generated. It is possible, altho unlikely, that it may repeat itself eventually.
May I suggest, if you don't want to take the approach of having folders to separate the files, you use:
while (file_exists($_FILES['file']['name'])) {
//If exists, then just add a random string to the end of the file
//This is just an example of an approach, what this will do is make your file look like: filename.jpg.aksjwq,
//The rest is homework for you, to work an improved solution, I'm just here to give you the idea :P
$_FILES['file']['name'] .= randomString(6);
}
function randomString($length = 6) {
$str = "";
$characters = array_merge(range('A','Z'), range('a','z'), range('0','9'));
$max = count($characters) - 1;
for ($i = 0; $i < $length; $i++) {
$rand = mt_rand(0, $max);
$str .= $characters[$rand];
}
return $str;
}
Yes, it's a possibility, tmp != unique
Avoid using tmp_name as its name, either use its real name or hash the file to get its checksum (> sha256) then use that, if you want it short just store it as an auto-incremented number, then use base_convert(), or a lib like hashids.
Good morning guys. I have created two php files that successfully upload files to my server. One file is the visual part for my website called upload.php and the other is the upload file part called upload_file.php.
the code to upload my files is
move_uploaded_file($_FILES['file']['tmp_name'],"./medetrax_backup/{$_FILES['file']['name']}");
This works perfectly however it lets me upload any file type. So since I want to only allow zipped folders i tried this if statement.
if($type=="application/zip" ){
move_uploaded_file($_FILES['file']['tmp_name'],"./medetrax_backup/{$_FILES['file']['name']}");
echo "<div id='mes'> File upload complete</div>";}
else{
echo "<div id='mes'>This file type cannot be uploaded. Only zip folders with the naming convention INITIAL.DATE.TIME are accepted</div>";
}
where $type=$_FILES['file']['type'];
But now it doesnt let me upload any files not even zipped ones. So what do i need to put in my if statement to only allow zipped folders to be upload? And if your really good guys what do i need to put in my if statement to allow only zipped foleders with the naming convention of USERINITIAL.DATE.TIME or USERINITIAL/DATE/TIME or can this not be done?
You can use this solution
$fileName = strtolower($fileName);
$allowedExts = array('zip');
$extension = explode(".", $fileName);
$extension = end($extension);
if(in_array($extension, $allowedExts))
{
//move file to custom folder
}
IMPORTANT *
Never not use from mime time for identification file type,because it bypass with tamper data.
Best way:
Move your all uploaded file into out of public_html,and Always rename file name,when you want upload this.
And so,save uploaded file name into database,and read file from one php file,for example:
read.php?id=5
in your read.php file,you should get id number and search on database for it,then,return file name from db and download or read this file with read.php file.
Due to some discussion on this thread, heres a little bonus info.
Generally speaking, it's really, really hard to determine if a file is actually the kind of file we want. You can check the mime type, which can be modified by the client. You can check the file extension, which can also be modified by the client- Vice versa.
You can even check the first few lines of a file, which typically contains some sort of header, explaining what kind of file we'r handling. But still then, the file might be modified by some evil genius making the executing program buffer overflow or exploits some library used, to open/view/taste/throw the file.
Lets check both file extension and mime.
First, the extension.
$extension = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
$isZipExtension = ( $extension == "zip" ? true : false );
On your "$_FILES" array, you have a index called "Type".
Now, you would like to restrict the upload to only accept Zip files.
Theres a few types defining a potential zip file. So lets create an array.
$zipTypes = array('application/zip', 'application/x-zip-compressed',
'multipart/x-zip', 'application/x-compressed');
Now, lets check if the type uploaded, is part of the array.
$isZipFile = in_array( $_FILES['file']["type"], $zipTypes );
If, the file is in the array, do your upload process.
if( $isZipFile && $isZipExtension) {
move_uploaded_file($_FILES['file']['tmp_name'],"./medetrax_backup/{$_FILES['file']['name']}");
echo "<div id='mes'> File upload complete</div>";
} else {
echo "<div id='mes'>This file type cannot be uploaded. Only zip folders with the naming convention INITIAL.DATE.TIME are accepted</div>";
}
All together
$zipTypes = array('application/zip', 'application/x-zip-compressed',
'multipart/x-zip', 'application/x-compressed');
$extension = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
$isZipExtension = ( $extension == "zip" ? true : false );
$isZipFile = in_array( $_FILES['file']["type"], $zipTypes );
if( $isZipFile && $isZipExtension) {
move_uploaded_file($_FILES['file']['tmp_name'],"./medetrax_backup/{$_FILES['file']['name']}");
echo "<div id='mes'> File upload complete</div>";
} else {
echo "<div id='mes'>This file type cannot be uploaded. Only zip folders with the naming convention INITIAL.DATE.TIME are accepted</div>";
}
Hope it helps.
Ironically, you should never use the type key to check the type of file being uploaded. That's because the value of that key is set by the client and can be trivially spoofed.
What you should be doing instead is checking the file extension (which at least makes sure that no well-configured program on your server will treat the upload in an unexpected manner):
$ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
$allowed = ['zip'];
if (in_array($ext, $allowed)) {
// process the file
}
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.
I have a file hosting website and everything works except one thing. I cannot upload two files with the same name. I want to be able to overright the name of a new file with a format like file_2.ext i believe this can be done with the file_exists() function but I am having trouble getting it to work. I will post the system I use to get files to upload below.
System:
if (move_uploaded_file($tmp_location, $location . $name)) {
echo'<br>Upload was successful';
}
I tried doing:
if (isset($_POST['submit_file']) &&!file_exists($name)) {
move_uploaded_file($tmp_location, $location . $name)
echo'<br>Upload was successful';
} else if (isset($_POST['submit_file']) &&file_exists($name)) {
!move_uploaded_file($tmp_location, $location . $name);
echo 'File exists';
}
The method above did not post the error message.
Try something like this. You will need to find a unique filename. You don't want to let it go indefinitely though, so add a limit to it.
function get_unique_filename($filename) {
if (!file_exists($filename)) return $filename;
$limit = 200;
$path = dirname($filename).DIRECTORY_SEPARATOR;
$basename = basename($filename);
$parts = explode('.', $basename);
$ext = array_pop($parts);
$base = implode('.', $parts);
for ($i=2; $i<$limit+2; $i++)
if (!file_exists($path.$base.'_'.$i.'.'.$ext))
return $path.$base.'_'.$i.'.'.$ext;
return false;
}
if (isset($_POST['submit_file'])) {
$new_path = get_unique_filename($location.$name);
if ($new_path && !move_uploaded_file($tmp_location, $new_path))
echo 'Could not upload file.'
else
echo '<br/>Upload was successful';
}
The way you're tackling it is wrong in my opinion , even if you need the user to have the "original" filename, you can't show them "file2.ext" if they uploaded "file.ext", that's just confusing and feels so wrong, take this example
Your server has files named file.ext, file2.ext and file4.ext and
your user uploads a file named file.ext, how would you handle that ? will you iterate over the pattern file*.ext and then add one to the biggest number ? or look for holes
and name it file3.ext ?, or will you append a random number to the file ? how will you handle collisions ? keep looping till it works ? it's just too much pain and might end up making your server really busy if you handle lots of uploads !
What I would do, is use uniqid for filenames on your filesystem, then have a DB that links original filenames to the UUIDs.
This way, all your users are able to upload an file named "file.ext", also it's cleaner to handle files since everything will be unique and their names will have the same length, and no need to worry about filenames in who knows what encoding, etc..
It'll also give you a bonus of being able to change UUIDs and show the same filenames to the user without worrying what they see, this way you're having a layer between the actual files on your FS and what the user perceives files.