I have learning portal(LMS) where I will upload documents, images, videos etc to create content. If the file being uploaded has a chinese name then it is not getting uploaded. Instead a corrupted file with junk name is uploaded.
For example, I tried to upload a file named 地球科学.jpg. But on the server I got this file as 地çƒç§‘å¦.jpg. Also the uploaded file is corrupted in the server.
I want this file to get upload with the same name on the server.
Because I want to search for these files and reuse later for creating content.
FYI:
I have XAMPP server installed on Windows XP.
Chinese, Korean, and Japanese language packs installed.
Thanks for your answers.
AFAIK ntfs can't handle some characters on the filesystem. I would suggest to store the file with a generic name.
for example you could create a table with two columns: name and file, as name you save the original name, and as file you set something like md5(name).
If you need the name to search for it use a database to store name information and the file location and save the file using your own convention.
Example
// sql entry
original name = 地球科学.jpg
path = /some/place/1.jpg
When you search you use the db to locate a given file name and location. Separation storage logic is something common when building image storage solutions not only for naming problems but also for limitations/spped considerations in terms of the number of files that accumulate in folders.
Use iconv or mb_convert_encoding to change character string encoding.
// Upload the file into the temp dir
$target_path = "uploadfiles/";
$target_path .= $_FILES['fileField']['name'];
// iconv()
move_uploaded_file($_FILES['fileField']['tmp_name'], iconv("UTF-8", "big5", $target_path))
// mb_convert_encoding()
move_uploaded_file($_FILES['fileField']['tmp_name'], mb_convert_encoding($target_path, "big5", "UTF-8"))
Make sure the page displaying the form is rendered in utf-8 or higher, usually this does the job, you can also choose to use the accept-charset attribute of the form element to indicate the posted data is sent as the specified charset.
Not sure if this all will do the job, let me know.
I think you might want to use somekind of database solution, especially when you need to search files later on. With database you can avoid I/O overhead.
I think you must learn/understand what character set the file is in before you can work out how to handle the upload. I'm afraid I'm not too familiar with non-european character sets and don't know which are most widely used.
UTF-8 should be a safe bet to handle almost whatever you care to throw at it. There's some relevant information that could be useful in terms of configuring your application in a post I wrote recently on my blog: How to Avoid Character Encoding Problems in PHP
Related
I have used this step echo $_FILES["fileField"]["tmp_name"]; but result like this.
C:\xampp\tmp\phpA9EE.tmp
How can i get exact file path?
An uploaded file does not have a "full path", other than temporary location where PHP has stored it during the upload process.
For the security of users, the browser sends only a filename of where it came from on the remote computer; for your security, you should not blindly use this (security rule of thumb: anything sent by the user is suspect and could be used to attack your system). You might want to filter it through a whitelist (e.g. remove anything other than letters and numbers) and use it as a "friendly" upload name, or you might want to ignore it completely. The browser also sends a file type (e.g. image/jpeg); again, this should not be trusted - the only way to know the type of a file is to use a command that looks at the content and validates it.
As far as PHP is concerned, what has been uploaded is a chunk of binary data; it saves this to a randomly named file, which is the path you have echoed there. The PHP manual has an introduction to how this works.
With that path you can do one of two things:
validate with is_uploaded_file(), and read the data with file_get_contents() or similar
use move_uploaded_file() to put it in a permanent location of your choice
I want to allow registered users of a website (PHP) to upload files (documents), which are going to be publicly available for download.
In this context, is the fact that I keep the file's original name a vulnerability ?
If it is one, I would like to know why, and how to get rid of it.
While this is an old question, it's surprisingly high on the list of search results when looking for 'security file names', so I'd like to expand on the existing answers:
Yes, it's almost surely a vulnerability.
There are several possible problems you might encounter if you try to store a file using its original filename:
the filename could be a reserved or special file name. What happens if a user uploads a file called .htaccess that tells the webserver to parse all .gif files as PHP, then uploads a .gif file with a GIF comment of <?php /* ... */ ?>?
the filename could contain ../. What happens if a user uploads a file with the 'name' ../../../../../etc/cron.d/foo? (This particular example should be caught by system permissions, but do you know all locations that your system reads configuration files from?)
if the user the web server runs as (let's call it www-data) is misconfigured and has a shell, how about ../../../../../home/www-data/.ssh/authorized_keys? (Again, this particular example should be guarded against by SSH itself (and possibly the folder not existing), since the authorized_keys file needs very particular file permissions; but if your system is set up to give restrictive file permissions by default (tricky!), then that won't be the problem.)
the filename could contain the x00 byte, or control characters. System programs may not respond to these as expected - e.g. a simple ls -al | cat (not that I know why you'd want to execute that, but a more complex script might contain a sequence that ultimately boils down to this) might execute commands.
the filename could end in .php and be executed once someone tries to download the file. (Don't try blacklisting extensions.)
The way to handle this is to roll the filenames yourself (e.g. md5() on the file contents or the original filename). If you absolutely must allow the original filename to best of your ability, whitelist the file extension, mime-type check the file, and whitelist what characters can be used in the filename.
Alternatively, you can roll the filename yourself when you store the file and for use in the URL that people use to download the file (although if this is a file-serving script, you should avoid letting people specify filenames here, anyway, so no one downloads your ../../../../../etc/passwd or other files of interest), but keep the original filename stored in the database for display somewhere. In this case, you only have SQL injection and XSS to worry about, which is ground that the other answers have already covered.
That depends where you store the filename. If you store the name in a database, in strictly typed variable, then HTML encode before you display it on a web page, there won't be any issues.
The name of the files could reveal potentially sensitive information. Some companies/people use different naming conventions for documents, so you might end up with :
Author name ( court-order-john.smith.doc )
Company name ( sensitive-information-enterprisename.doc )
File creation date ( letter.2012-03-29.pdf )
I think you get the point, you can probably think of some other information people use in their filenames.
Depending on what your site is about this could become an issue (consider if wikileaks published leaked documents that had the original source somewhere inside the filename).
If you decide to hide the filename, you must consider the problem of somebody submitting an executable as a document, and how you make sure people know what they are downloading.
PHP ships with various methods of identifying the type of a file, but is it possible to identify a data type when the file in question only exists as a binary string representation and not as an actual file on disc?
The reason for this is I'm doing some maintenance work on a CMS where the previous developer, being a bit of a wally, decided to store image data into the system as database BLOBs. My current project is dumping the BLOBs out into files, and saving the path to the files into the database in place of the BLOBs.
As I said, my predecessor was a bit of a wally and not only did he store all this data as BLOBs, he also didn't save the datatype of the data anywhere.
The migration utility I wrote for part of this project saves the file to disc without an extension, tries to determine the type of the file with exif_imagetype() and if it manages to identify the file type, renames the file with the correct extension.
However, the classes that use the image data also need updating so they can continue to function with paths and files on disc instead of BLOBs.
The methods that create and update images expect binary strings (to BLOB into the database) and in an ideal world I'd rather rewrite these methods to use is_uploaded_file, move_uploaded_file, etc. However, there's no evidence anywhere in the class of direct manipulation of the $_FILES array so the filedata obviously comes from outside the classbut given how convoluted the code is (and no comments to help out) I can't find it.
As a stopgap solution until I finally track down the actual file upload management code, I plan to manipulate the file data as strings in the class as is currently done, but saving the strings to files instead of into the database. This should minimize the impact on other parts of the codebase that are relying on this class.
I could just do what the migration script is doing and rename the file after saving and then identifying it, but this could prove problematic in the case where there is already a file there. I'd rather know what the data type is before I commit the data to disc.
finfo_buffer() is what you want. You can pass it your string and it will tell you what the file type is based on your mime.magic file.
More info here: http://us3.php.net/manual/en/function.finfo-buffer.php
You can use finfo_buffer. It works on strings rather than on-disk files.
If you are working only with images, you can find the filetype looking in the first few bytes of the string - PNG files begin with 0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A, GIF - with GIF, and JPEG contains FFD8 in the header. I've wrote script for parsing headers of those 3 types, but since I don't have it here, I'll update my answer as soon as I get it
A drop-box directory for image files has collected variants by letter-case, for example:
Bonsai.jpg, BONSAI.jpg, Bonsai.JPG, bonsai.jpg
I am making a web app using CodeIgniter to manage these documents on a remote server. This means using
file_exists() or is_file() to verify
a file's presence
HTML img tag to display the file graphically
But both these tools use the first match they find, regardless of case. How can I deal with this?
(I noticed this similar question as this, but for Delphi instead of PHP.)
But both these tools use the first match they find, regardless of case
They definitely shouldn't - at least not on a file system that is case sensitive, like Linux's default file system (is it still called ext2?). While it's questionable practice to have those four file in the same directory IMO, neither file_exists() nor the serving of web resources should show the behaviour you describe.
It's different on Windows: FAT and NTFS are not case sensitive. In your example, only one of the four files you mention can exist in the same directory.
When accepting images I always rename them, for example using CI's encrypt filenames option of the File Upload class to avoid these kind of problems. Otherwise it can turn in to a big headache.
EDIT: added my comment on the OP below
You can easily write a script that puts all filenames in to an array, identify duplicates and append _1 to their name. Now you have just unique filenames. Then you convert all to lowercase. For all existing files and new ones you encrypt the filenames to a 32 character string. Batch processing of filenames like this is actually quite easy. Just keep a back up of all files just in case, and very little can go wrong.
Codeigniter has some useful functions like the file helper's get_filenames() which puts all files in a specified directory in to an array, and the security helper's dohash() which would encrypt the filenames. For future uploads set encrypt_name preference to TRUE
I am in the middle of making a script to upload files via php. What I would like to know, is how to display the files already uploaded, and when clicking on them open them for download. Should I store the names and path in a database, or just list the conents of a directory with php?
Check out handling file uploads in PHP. A few points:
Ideally you want to allow the user to upload multiple files at the same time. Just create extra file inputs dynamically with Javascript for this;
When you get an upload, make sure you check that it is an upload with is_uploaded_file;
Use move_uploaded_file() to copy the file to wherever you're going to store it;
Don't rely on what the client tells you the MIME type is;
Sending them back to the client can be done trivially with a PHP script but you need to know the right MIME type;
Try and verify that what you get is what you expect (eg if it is a PDF file use a library to verify that it is), particularly if you use the file for anything or send it to anyone else; and
I would recommend you store the file name of the file from the client's computer and display that to them regardless of what you store it as. The user is just more likely to recognise this than anything else.
Storing paths in the database might be okay, depending on your specific application, but consider storing the filenames in the database and construct your paths to those files in PHP in a single place. That way, if you end up moving all uploaded files later, there is only one place in your code you need to change path generation, and you can avoid doing a large amount of data transformation on your "path" field in the database.
For example, for the file 1234.txt, you might store it in:
/your_web_directory/uploaded_files/1/2/3/1234.txt
You can use a configuration file or if you prefer, a global somewhere to define the path where your uploads are stored (/your web directory/uploaded files/) and then split characters from the filename (in the database) to figure out which subdirectory the file actually resides in.
As for displaying your files, you can simply load your list of files from the database and use a path-generating function to get download paths for each one based on their filenames. If you want to paginate the list of files, try using something like START 0, LIMIT 50; in mySQL. Just pass in a new start number with each successive page of upload results.
maybe you should use files, in this sense:
myfile.txt
My Uploaded File||my_upload_dir/my_uploaded_file.pdf
Other Uploaded File||my_upload_dir/other_uploaded.html
and go through them like this:
<?php
$file = "myfile.txt";
$lines = file($file);
$files = array();
for($i=0;$i<=count($lines)-1;$i++) {
$parts = explode($lines[$i]);
$name = parts[0];
$filename = parts[1];
$files[$i][0] = $name;
$files[$i][1] = $filename;
}
print_r($files);
?>
hope this helps. :)
What I always did (past tense, I haven't written an upload script for ages) is, I'd link up an upload script (any upload script) to a simple database.
This offers some advantages;
You do not offer your users direct insight to your file system (what if there is a leak in your 'browse'-script and you expose your whole harddrive?
You can store extra information and meta-data in an easy and efficient way
You can actually query for files / meta-data instead of just looping through all the files
You can enable a 'safe-delete', where you delete the row, but keep the file (for example)
You can enable logging way more easily
Showing files in pages is easier
You can 'mask' files. Using a database enables you to store a 'masked' filename, and a 'real' filename.
Obviously, there are some disadvantages as well;
It is a little harder to migrate, since your file system and database have to be in sync
If an operation fails (on one of both ends) you have either a 'corrupt' database or file system
As mentioned before (but we can not mention enough, I'm afraid); _Keep your uploading safe!_
The MIME type / extension issue is one that is going on for ages.. I think most of the web is solid nowadays, but there used to be a time when developers would check either MIME type or extension, but never both (why bother?). This resulted in websites being very, very leaky.
If not written properly, upload scripts are big hole in your security. A great example of that is a website I 'hacked' a while back (on their request, of course). They supported the upload of images to a photoalbum, but they only checked on file extension. So I uploaded a GIF, with a directory scanner inside. This allowed me to scan through their whole system (since it wasn't a dedicated server; I could see a little more then that).
Hope I helped ;)