Writing a small app that (among other things) lets users upload a file (like an image, a .doc or a text file) as part of their posting/submission.
Our current prototype just dumps the file into /{app_root}/files/, but of course, anyone can get to that even if they are not logged in or using the system. The goal is to only grant access (view access) to the files if user is logged in and does in fact have access to the post that the file belongs to.
So, in short, I am looking for a good way to do this.
I am thinking of either creating a folder outside the /web/ (http) folder and then having PHP render it somehow using header() commans, or, maybe just dumping the file into the database? I have never done either one, however.
While I suspect I can figure it out eventually, there are just too many smart people on here that I was figuring someone will know of some sort of existing class or function library that does this already?
You have to do the following:
Move all the files out of the webroot. You could disable access to the folder with .htaccess, but it is not worth the hassle and potential security risk. Just move it out there.
Keep a table of the files uploaded, storing the user's original file name there. Rename the file to $id.$ext and so on. In short, you don't want to use the user's file name in your system.
Have a script, download.php or whatever, get the file's ID, verify who is logged in, and if everything checks out, fetch the file, read it out to the browser, and send the appropriate download headers.
These headers would be something like:
header('Content-type: application/octet-stream');
header('Content-disposition: attachment; filename=usersuppliedname.txt');
header("Content-Length: " . filesize('../safefiles/1.txt'));
header("Content-Transfer-Encoding: binary");
readfile('../safefiles/1.txt');
exit;
You can then get more fancy if you want to allow resuming files and such, but the above should do it.
Related
Good evening.
I am using Yii framework and mPDF library to generate some PDF files via Ajax script and I need to force "save as" dialogue in users' browsers.
I know how to solve this issue with a single user dowloading a single file.
Does anyone have a tip on how could I make it all work on high-load system (e.g. several users trying to generate and download a PDF will attempt to access one temp file which would cause an error)?
Should I generate a separate file for each session? And which way would be nice for cleaning these temp files?
Thank you for your help.
you should use tempnam http://www.php.net/manual/en/function.tempnam.php to generate the temp files. they'll be uniquely named, so it'll be easy to make one per session. Just delete as normal when you're done with them.
Make an invisible iframe. From JS set that iframe's src to the script on your server that generates the PDF.
<iframe src="http://yoursite.com/download-file.php?report=pdf¶m1=value1¶m2=value2..." width="1" height="1"></iframe>
Then (and I'm not sure how you do this with mPDF) the point is to output the file from script directly into the browser. It's something like this:
<?php
$x = some_function($_GET['param1'],$_GET['param2', ...); // PDF GEN. ROUTINE, BASED ON REQUEST DATA, HOWEVER YOU DO IT
header('Content-type: application/pdf');
echo $x;
That should solve all your concerns.
First of all, I'd recommend you to generate a different temp file for each generated PDF, in order to avoid any possible error like one user downloading somebody else's PDF, etc.
To clean up the temp directory, I'd use a cronjob that deletes all files older than N days.
In order to "force save dialog", you have to set the Content-disposition header to attachment:
header('Content-Disposition: attachment; filename="myfile.pdf"');
I'm building a file sharing site, and I'm thinking, I want my users to be able to upload and share anything.
Sounds dangerous, I know. But, is there a method to allow this to be possible? For example, forcing the download when the user requests the link with a mime type? Rather than "running" something on the page.
Any ideas how to allow any file type without the security issue.
Thanks
Store the file on a location not accessible by the user through the browser. (so above the document root)
when loading a file, use the readfile() function.
Set correct headers, including these:
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=[INSERT FILENAME HERE]");
And also use the correct content-type header for each type of file
If you only want it to be dangerous to other users (as they will be sharing virus ridden files in no time), but provide some protection for your web server from code injection attacks, you might consider storing the uploads in a database BLOB field. That should at least make it harder to inject code that will run on the server.
You can allow users to upload anything by using a simple HTML multipart form along with PHP's $_FILES. Only issue I see is file size limits.
You than can simply post a link to any of these files. It's the user's browsers settings that determine if a file is executed or downloaded, not yours. So you can suggest that they right click on them, and select 'save to', but besides that it's limited.
Any ideas how to allow any filetype without the security issue.
You say it yourself. It's like wanting to make scrambled eggs, without wanting to break the eggs.
I'm looking for a way to hide the source of my download. I'm surprised this is not more covered, but it also makes me wonder whether it's possible.
(Edit: By hide I mean make it difficult or impossible for end user to find a direct link to the file. They will thus be forced to actually be on the page, clicking it, for it to work.)
I found a script to force download files that are locally stored. The way I see it, it hides the true source (at least it's not in view source or download history).
http://w-shadow.com/blog/2007/08/12/how-to-force-file-download-with-php/
So this works, I made it into a function that gets a linkID, and check that with a DB for the actual file-source. Hooray!
Only what if your downloads are on another server? Then you can't use most of the functions used here (like filesize, isreadable, fopen, ...). I'm not proficient enough to decide whether it is possible/feasible to make this work cross-server.
I realize that probably my webserver will lose bandwidth even though files aren't stored there, that's not a big issue.
Any info on the subject would be greatly appreciated. I prefer PHP, but I can work with whatever you give me, I really have no idea about this one.
You mean you want to hide the path of files stored on your server? If this is the case, simply store the files outside of your web root, and serve the files with a PHP script which will make use of readfile() + header() of appropriate headers depending on whether you are serving the file for opening or forced download. See http://php.net/readfile for plenty of examples on forced download scripts.
Sorry not possible. You HAVE to tell the browser where the resource is located so any savy user can simply decode the address or scan the HTTP request or their firewall logs or download history in the browser.
If you're trying to hide the path on your server then URL rewriting with mod_rewrite or aliases or other similar method should be sufficient.
UPDATE: Ok if using your own bandwidth is not an issue then all you need to be doing is outputting the files binary content to the browser and setting the relevant HTTP headers (ie, Content-Type and Content-Disposition). If the files MUST be stored remotely then you'll need your script to download and read them on-the-fly using CURL or similar before outputting the content.
If you mean hide the script or the directory the file is coming from, it's a simple answer--you can't.
BUT, you can make it only accessible on your terms, like using a script to render the file (as you have) but only when specific criteria are met. Alternatively you can move the file to a temp/secured directory and allow them direct access, but this also means waiting for the move, providing (what is deemed) reasonable and fair time to download the file, then removing it/deleting it when it's done.
You could use cURL to serve as a pass-thru for the content. This would conceal the source of the actual data, and allow you to secure it any way you choose. It would also take a lot of bandwidth, roughly 2 times the size of all downloaded files.
Give this a shot and let me know if it works?
if ($passesallyoursecurity) {
set_time_limit(0);
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false);
header("Content-Type: application/download");
header("Content-Disposition: filename=filetheyget.ext");
$ch = curl_init("http://remotedomain.com/dir/file.ext");
curl_exec($ch);
curl_close($ch);
exit();
}
I would recommend that you use a die() message without any HTML, whatsoever. in the document.
Then, insert the IP Addresses you want the webpage to decline. I would break the IP Addresses you don't like in an array(). Then use an "if" construct to see if any of those IP Addresses are lurking.
$decline_ips = array('ip_1' => '127.0.0.1');
if ($_SERVER['REMOTE_ADDR'] == $decline_ips['ip_1']) {
die("You aren't permitted direct access to this page.\n\n\n\n
Sources are blank.");
}
It works like a charm! And for XTRA-XTRA security, I would recommend inserting the IP address that isn't allowed (when they visit) to the Database so that whenever they try again, both the source is covered and the entire document.
But, you can just use the script I have posted on a different part of the document.
Just a quick question (maybe not)
I'm looking to add to my website a way for a customer to login and once logged in they are able to select their invoices which i will upload to my server.
My question is what is the best way to go about storing the files and accessing them.
i've done some googling and haven't been able to find much in the way of pointing me in the right direction.
Thanks in advance
Upload them to an unaccessible directory (outside of the www root or protected by htaccess) and serve them with PHP.
This can be done like so:
$file = '/path/to/yourpdf.pdf';
header('Content-type: application/pdf');
header('Content-Length: ' . filesize($file));
readfile($file);
Use PHP and SQL to determine what $file should be and whether or not the user has permission to view the file.
One recommendation I have is to ensure you do not store the actual file in the db. Make sure all you are storing is a pointer (the hyperlink) to a file that is stored somewhere on your network.
I've inherited an application with a glaring security hole.
It has session-based security, but file uploads (which are user specific) are not secured in any way and they are stored in the public file tree.
Filenames do not follow any convention as such, making them hard to guess, but the data is sensitive and thus I need to implement a security measure to prevent unauthorized file access.
Moving the location of the files is not really an option, so I'm looking at a htaccess solution to forward requests to a php handler script.
Does anyone have experience in implementing this type of thing or any good alternative solutions? Specific examples of .htaccess syntax greatly appreciated, as I'm struggling in this area.
Don't really understand why moving them isn't an option, since pushing requests for them to a handler means it no longer matters where they're stored. But you're the man on the scene.
.htaccess looks like:
RewriteEngine on
RewriteRule path/to/where/these/files/live/(.*) /handlerscript.php/$1
Then you pick up the remaining file path and name from $_SERVER['PATH_INFO'].
Well, you could make apache parse .jpg file's for a certain folder adding the following to your .htaccess
AddHandler php5-cgi .jpg
then you could set a file of php to parse the request the way chaos was recomending you and doing a certain validation, then just return jpeg headers along with the correct picture u'd like to display
here's an example
<?php
if($validUser)
{
header("Cache-control: No-cache");
header("Pragma: No-cache");
header("Content-Type: image/jpeg");
//correct picture address
$img = imagecreatefromjpeg("2326_b_lil.jpg");
imagejpeg($img);
}
else
{
//code for error image
}
?>
please let me know if you want a more extensive example
I think you may need to write a script that will serve the images, then use htaccess to completely restrict access to the actual images from a browser.
The script can take in the web path to the image, decide if the user has access, then use something like fpassthru to feed an actual image to the browser.
All references to the images would need to be modified, however, to reference the serving script.
So instead of accessing the images with /images/123/5423453245.jpg,
it would be /image.php?images/123/5423453245.jpg
Or something similar to that.