Secure PHP file uploading - php

I'm trying to develop a file uploading module on our new site that allows you to upload any file to our servers. The uploaded file is uploaded to /files, in which the following .htaccess to prevent users from executing i.e a .php file:
<Files *.*>
ForceType applicaton/octet-stream
</Files>
This triggers the browsers download window (at least in FF and Safari), but is it safe to assume the file won't be run on the server using this method? If not, how would you implement such a solution?

I think the safest thing is to restrict 100% web access to the directory, and have a script like download.php through which you pass a file id that then fetches the appropiate file and outputs it to the browser. However, I am pretty sure that what you have will work and is safe.

is it safe to assume the file won't be run on the server using this method?
Kind of, but it depends on what other directives are present in your config; maybe there are other rules set up to allow PHP files to run. If the only way you're enabling PHP is by keying the PHP handler on file type, that should stop PHP executing.
However, stopping PHP executing is just one of your worries. If people upload files that contain active content, such as HTML or Flash — even if the filetype says it's an innocent image — they can gain control of other users' sessions on your site through cross-site scripting (XSS). See Stop people uploading malicious PHP files via forms for some discussion of this.
A ‘download.php’ interface that uses Content-Disposition to always trigger the download box, coupled with storing the files under non-user-supplied filenames like ‘1234.dat’, is much safer.

I think you actually want this:
<Directory /path/to/files>
SetHandler default-handler
</Directory>
What you have might work in practice, because the server is configured by default not to execute anything unless specifically told to do so, but it doesn't really guarantee that nothing will be executed. ForceType just sets the content type for static files (I'm not sure, but I doubt that it affects executable scripts).

Seconding Paolo's answer, move your files directory out of the accessible path. You can then write the download.php script using PEAR's HTTP_Download module to serve the files.

I agree with Paolo, his way is more secure. There is always the issue of someone exploiting your PHP files to execute an uploaded one. Bad Example:
include_once("/modules/".$_GET["module"].".php");
Where someone passed in module=../Files/exploit

For maximum security, you shuold have the folder containing the uploaded files be mounted from a separate partition with the no-exec flag.

Related

Is it enough security to esape file names and prevent opening them?

I have a project where there is Log in/out functionality, and authenticated users can upload, download and delete files they own.
My question is: is it enough security for the files part of my project to escape
file names with htmlentities() and to prevent users from opening the directory where files are located with
<Directorymatch /cloud/>
Order deny,allow
Deny from all
</Directorymatch>
This means that if a .php file is uploaded, it cannot be run on my server by a user. Also if the file name contains scripts / html then it does not un.
What else am i missing in terms of security? I probably will be the only user of this bootleg "google drive", but i want to take security seriously. What am i missing?
You will need to ensure that your uploaded file are well sanitized and validated before being uploaded.
finfo_* library would be good but it will work with php >= 5.3.0 versions. The Stackoverflow link below has the
best solution on how to validate and secure your file upload using the best security measures.
Source Link
Again you may need to turn off php engine in the upload directory. so you can create a php.ini file and enter this
line of code
engine = off
Updated section
You might use shell_exec() and exec() for testing things locally but you have to disable them
in production because an attacker can use those shell command to obtain your system or files informations which might lead
to compromise of your entire network.
For Instance take a look at the shell_exec() code below
<?php
// Use ls command to shell_exec
// function
$output = shell_exec('ls');
// Display the list of all file
// and directory
echo "<pre>$output</pre>";
?>
Output:
transaction.php
index.html
moneyupdate.php
Now take a look at Exec() Code below
<?php
// (on a system with the "iamexecfunction" executable in the path)
echo exec('iamexecfunction');
?>
Output:
transaction.php
So in a nutshell its up to you to decide. If you are on share hosting please you will need to disable them because your
hosting neighbor might inter-fer but if you are in a VPS or Dedicated, you might try it out on your own

Safe general config file for webapp

I would like to keep all config options for a webapp in one file. (pathes, passwords, options which are read by php, sass (during compilation), maybe grunt,..)
I like the JSON format since its very clear and almost anything can parse json. But by default .json files can be downloaded.
Can I safely prevent that by giving the file a .json.php extension?
What are the drawbacks? Better Approaches?
To prevent the file being downloaded, generally the way to go is to store it in a directory that is not served by the web server. I don't know what setup you're in, but assuming an Apache setup, if for example your .php files are served from a directory /home/user/htdocs, you could create a directory /home/user/config, ensure that it is readable by the webserver, and store the .json files there.
Another approach, again assuming Apache, would be to create an .htaccess file containing the following (inspired by this answer):
RedirectMatch 404 \.json$
This would not only prevent downloading any and all .json files in the directory, but hide their very existence.
It might just be possible to do it the way you suggested, by storing the file with a .json.php extension, although this would not be a recommended approach. For this to work, the file has to be valid PHP but it must obviously be valid JSON as well and we are hampered somewhat by the fact that JSON does not allow comments. Something like the following would stop the PHP interpreter soon after the start of the file, before spilling your secrets:
{
"<?php exit('Access denied'); ?>": null,
"password": "secret"
}

Display source of PHP of files

Im working on an upload script, and i want a user to be able to upload any file.
I had it al working on localhost, i added
php_flag engine off
AddType text/plain php html shtml php5 php4 php3 cgi asp aspx xml
to my htaccess in the upload folder, and it showed the source of PHP, html and all other files. Exactly as i wanted to.
Now i tried to upload it to a real webserver, and unfortunately my host does not allow such .htaccess files.
I tried openinging the files with file_get_content() and fopen() and giving them a text/plain header.. but nothing works. It first executes the scripts and shows the output in my textarea.
Do you guys have any suggestions on how i can fix this without .htaccess ?
Thanks!
Don't upload files into the webroot and let people access them directly. As you say, .php scripts (and probably a lot more) get executed that way. A classic way for arbitrary code execution attacks.
Store uploaded files outside the webroot where they're not publicly accessible and create a script that allows users to download the files, for example using readfile or Apache mod_xsendfile, after having done the necessary permission checks.
Also see Security threats with uploads.

Securing upload form at php

I am making a feature to my site so that users can upload files (any type).
In order to secure the upload form, i made a blacklist of non-accepted filetypes. But in order to assure protection to my server (in case of uploading malicious scripts in any way) i thought to tar the uploaded files (using the tar class) so that they are stored as .tar zipped files.
So if the user wants to donwload it, then he will receive a .tar file.
My question is, is this secure enough? (since the files cannot be executed then).
[I have this reservation as i can see at the code of tar class, the "fread()"]
Thanks!
Two points, here :
Using a blacklist is a bad idea : you will never think to all possible evil filetypes.
Do not store the uploaded files into a public directory of your server :
Store those files to a directory that is not served by Apache, outside of your DocumentRoot.
And use a PHP script (even if Apaches cannot serve the files through HTTP, PHP can read them) to send those files contents to the user who wants to download them.
This will make sure that those uploaded files are never executed.
Of course, make sure your PHP script that sends the content of a file doesn't allow anyone to download any possible file that's on the server...
You can upload the files to an non web accessible location (under your webroot) and then use a download script to download the file.
The best way of handling uploaded files, in my opinion, is to place them in a folder that's not reachable through HTTP. Then when a file is requested, use a PHP file to send then download headers, the use readfile() to send the file to the user. This way, files are never executed.
That might work, assuming that you're users that will download the files can untar them (most non UNIX systems just have zip, I'd give them the option to download either format).
Also, i think its better to create a list of allowed files vs banned files. Its easy to forget to ban a specific type; whereas you will probably have a better idea of what users can upload
Dont block/allow files on extension. Make sure you are using the mime type that the server identifies the file as. This way its hard for them to fake it.
also, store the files in a non web accessible directory and download them through a script.
Even if its a bad file, they won't be able to exploit it if they can't directly access it .
When saving the files make sure you use these functions:
http://php.net/manual/en/function.is-uploaded-file.php
http://php.net/manual/en/function.move-uploaded-file.php
Dan

Stop people uploading malicious PHP files via forms

I have an upload form created in php on my website where people are able to upload a zip file. The zip file is then extracted and all file locations are added to a database. The upload form is for people to upload pictures only, obviously, with the files being inside the zip folder I cant check what files are being uploaded until the file has been extracted. I need a piece of code which will delete all the files which aren't image formats (.png, .jpeg, etc). I'm really worried about people being able to upload malicious php files, big security risk! I also need to be aware of people changing the extensions of php files trying to get around this security feature.
This is the original script I used http://net.tutsplus.com/videos/screencasts/how-to-open-zip-files-with-php/
This is the code which actually extracts the .zip file:
function openZip($file_to_open) {
global $target;
$zip = new ZipArchive();
$x = $zip->open($file_to_open);
if($x === true) {
$zip->extractTo($target);
$zip->close();
unlink($file_to_open);
} else {
die("There was a problem. Please try again!");
}
}
Thanks, Ben.
Im really worried about people being able to upload malicious php files, big security risk!
Tip of the iceberg!
i also need to be aware of people changing the extensions of php files trying to get around this security feature.
Generally changing the extensions will stop PHP from interpreting those files as scripts. But that's not the only problem. There are more things than ‘...php’ that can damage the server-side; ‘.htaccess’ and files with the X bit set are the obvious ones, but by no means all you have to worry about. Even ignoring the server-side stuff, there's a huge client-side problem.
For example if someone can upload an ‘.html’ file, they can include a <script> tag in it that hijacks a third-party user's session, and deletes all their uploaded files or changes their password or something. This is a classic cross-site-scripting (XSS) attack.
Plus, thanks to the ‘content-sniffing’ behaviours of some browsers (primarily IE), a file that is uploaded as ‘.gif’ can actually contain malicious HTML such as this. If IE sees telltales like (but not limited to) ‘<html>’ near the start of the file it can ignore the served ‘Content-Type’ and display as HTML, resulting in XSS.
Plus, it's possible to craft a file that is both a valid image your image parser will accept, and contains embedded HTML. There are various possible outcomes depending on the exact version of the user's browser and the exact format of the image file (JPEGs in particular have a very variable set of possible header formats). There are mitigations coming in IE8, but that's no use for now, and you have to wonder why they can't simply stop doing content-sniffing, you idiots MS instead of burdening us with shonky non-standard extensions to HTTP headers that should have Just Worked in the first place.
I'm falling into a rant again. I'll stop. Tactics for serving user-supplied images securely:
1: Never store a file on your server's filesystem using a filename taken from user input. This prevents bugs as well as attacks: different filesystems have different rules about what characters are allowable where in a filename, and it's much more difficult than you might think to ‘sanitise’ filenames.
Even if you took something very restrictive like “only ASCII letters”, you still have to worry about too-long, too-short, and reserved names: try to save a file with as innocuous a name as “com.txt” on a Windows server and watch your app go down. Think you know all the weird foibles of path names of every filesystem on which your app might run? Confident?
Instead, store file details (such as name and media-type) in the database, and use the primary key as a name in your filestore (eg. “74293.dat”). You then need a way to serve them with different apparent filenames, such as a downloader script spitting the file out, a downloader script doing a web server internal redirect, or URL rewriting.
2: Be very, very careful using ZipArchive. There have been traversal vulnerabilities in extractTo of the same sort that have affected most naive path-based ZIP extractors. In addition, you lay yourself open to attack from ZIP bombs. Best to avoid any danger of bad filenames, by stepping through each file entry in the archive (eg. using zip_read/zip_entry_*) and checking its details before manually unpacking its stream to a file with known-good name and mode flags, that you generated without the archive's help. Ignore the folder paths inside the ZIP.
3: If you can load an image file and save it back out again, especially if you process it in some way in between (such as to resize/thumbnail it, or add a watermark) you can be reasonably certain that the results will be clean. Theoretically it might be possible to make an image that targeted a particular image compressor, so that when it was compressed the results would also look like HTML, but that seems like a very difficult attack to me.
4: If you can get away with serving all your images as downloads (ie. using ‘Content-Disposition: attachment’ in a downloader script), you're probably safe. But that might be too much of an inconvenience for users. This can work in tandem with (3), though, serving smaller, processed images inline and having the original higher-quality images available as a download only.
5: If you must serve unaltered images inline, you can remove the cross-site-scripting risk by serving them from a different domain. For example use ‘images.example.com’ for untrusted images and ‘www.example.com’ for the main site that holds all the logic. Make sure that cookies are limited to only the correct virtual host, and that the virtual hosts are set up so they cannot respond on anything but their proper names (see also: DNS rebinding attacks). This is what many webmail services do.
In summary, user-submitted media content is a problem.
In summary of the summary, AAAARRRRRRRGGGGHHH.
ETA re comment:
at the top you mentioned about 'files with the X bit set', what do you mean by that?
I can't speak for ZipArchive.extractTo() as I haven't tested it, but many extractors, when asked to dump files out of an archive, will recreate [some of] the Unix file mode flags associated with each file (if the archive was created on a Unix and so actually has mode flags). This can cause you permissions problems if, say, owner read permission is missing. But it can also be a security problem if your server is CGI-enabled: an X bit can allow the file to be interpreted as a script and passed to any script interpreter listed in the hashbang on the first line.
i thought .htaccess had to be in the main root directory, is this not the case?
Depends how Apache is set up, in particular the AllowOverride directive. It is common for general-purpose hosts to AllowOverride on any directory.
what would happen if someone still uploaded a file like ../var/www/wr_dir/evil.php?
I would expect the leading ‘..’ would be discarded, that's what other tools that have suffered the same vulnerability have done.
But I still wouldn't trust extractTo() against hostile input, there are too many weird little filename/directory-tree things that can go wrong — especially if you're expecting ever to run on Windows servers. zip_read() gives you much greater control over the dearchiving process, and hence the attacker much less.
First you should forbid every file that doesn’t have a proper image file extension. And after that, you could use the getimagesize function to check whether the files are regular image files.
But furthermore you should be aware that some image formats allow comments and other meta information. This could be used for malicious code such as JavaScript that some browsers will execute under certain circumstances (see Risky MIME sniffing in Internet Explorer).
You should probably not rely just on the filename extension, then. Try passing each file through an image library to validate that its really an image, also.
I don't see the risk in having renamed php files in your DB...
As long as you're not evaluating them as PHP files (or at all, for that matter), they can't do too much harm, and since there's no .php extension the php engine won't touch them.
I guess you could also search the files for <?php...
Also: assume the worst about the files uploaded to your machine. Renamed the folder into which you're saving them "viruses" and treat it accordingly. Don't make it public, don't give any file launch permissions (especially the php user), etc.
You might also want to consider doing mime type detection with the following library:
http://ca.php.net/manual/en/ref.fileinfo.php
Now you are relying on your harddrive space for extracting. You can check fileheaders to determine what kind of files they are. there probably libraries for that.
offtopic: isnt it better to let the user select couple of images instead of uploading a zip file. Better for people that don't know what zip is (yes they exist)
If you set php to only parse files ending with .php, then you can just rename a file from somename.php to somename.php.jpeg and you are safe.
If you really want to delete the files, there is a zip library available to php. You could use it to check the names and extensions of all the files inside the zip archive uploaded, and if it contains a php file, give the user an error message.
Personally, I'd add something to the Apache config to make sure that it served PHP files as text from the location the files are uploaded to, so you're safe, and can allow other file types to be uploaded in the future.
Beaware of this Passing Malicious PHP Through getimagesize()
inject PHP through image functions that attempt to insure that images
are safe by using the getimagesize() function
read more here http://ha.ckers.org/blog/20070604/passing-malicious-php-through-getimagesize/
Better for your user logo use gravatar like here used by Stackoverflow ;)
Use getimagesize function.
Full procedure:-
1.) Extract extension of image/uploaded file and then compare extension with allowed extension.
2.) Now create a random string for renaming uploaded file. best idea is md5(session_id().microtime()).It can not be duplicated and if your server is very fast and can process less than a microsecond than use incremented variable and add them with string.
now move that file.
A tip
Disable PHP file processing in upload directory, it will always prevent you from any server side attack and if possible add your htaccess in root directory or in httpd config file and disable htaccess files from there now it solve your maximum problems

Categories