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.
Related
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 have a simple login / access control system to secure some restricted pages, but within these pages there are links that need to be secure, i.e. Word documents. Therefore if I keep these resources within the webroot they could accessible via a URL. What is the best method to secure these resources that are within the restricted page. I know I could password protect the folder but the user would then be challenged twice, one for the restricted page and then for the resource link. Any advice ?
You have a few options here, depending on your use-case.
Use PHP to serve the file. Basically, either intercept all attempts to read the file by PHP (using a mod_rewrite rule), or link directly to PHP and put the file(s) below the document root. Then use something like fpassthru to send the file to the browser. Note that you must properly set the content-type headers. Also note that this will eat up a lot of server resources since the server needs to read the entire file in PHP and send it, so it's easy, but not light.
$f = fopen('file.doc', 'r');
if (!$f) {
//Tell User Can't Open File!
}
header('Content-Type: ...');
header('Content-Length: '.filesize('file.doc'));
fpassthru($f);
die();
The main benefit to doing it this way is that it's easy and portable (will work on all servers). But you're trading off valuable server resources (since while PHP is serving the file, it can't be serving another page) for that benefit...
Use the web-server to send the file using something like X-SendFile (Lighttpd), X-SendFile (Apache2/2.2) or X-Accel-Redirect (NginX). So you'd redirect all requests to the file to PHP (either manually or rewrite). In PHP you'd do your authentication. You'd send the Content-Type headers, and then send a header like X-SendFile: /foo/file.doc. The server will actually send the file, so you don't have to (it's far more efficient than sending from PHP natively).
header('Content-Type: ...');
header('X-SendFile: /foo/file.doc');
die();
The main benefit here is that you don't need to serve the file from PHP. You can still do all of your authentication and logging that you'd like, but free up PHP as soon as you start transferring the file.
Use something like mod_secdownload (lighttpd) or mod_auth_token (Apache). Basically, you create a token in PHP when you generate the link to the file. This token is a combination of a MD5 of a secret password combined with the current timestamp. The benefit here, is the URL is only valid for how long you specify in the configuration (60 seconds by default). So that means that the link you give out will only be active for 60 seconds, and then any further attempts to see the content will generate a 400 series error (I'm not positive which off the top of my head).
$filename = '/file.doc';
$secret = 'your-configured-secret-string';
$time = dechex(time());
$token = md5($secret . $filename . $time);
$url = "/downloads/$token/$time$filename";
echo "Click Here To Download";
The main benefit to doing it this way is that there is very little overhead associated with the implementation. But you have to be comfortable with having URLs being valid for a set time only (60 seconds by default)...
Push it off onto a CDN to handle. This is like option #3 (the one above), but uses a CDN to handle the file serving instead of your local server. Some CDNs such as EdgeCast provide a similar functionality where you set a token which expires after a set amount of time. This case will work nicely if you have a lot of traffic and can justify the expense of a CDN. (Note: no affiliation with the linked CDN, only linked because I know they offer the functionality).
As far as how I'd personally do it, I've done all of the above. It really matters what your use-case is. If you're building a system that's going to be installed on shared hosts or multiple different servers which you don't control, stick to the first option. If you have full control and need to save server resources, do one of the other two.
Note: there are other options than these three. These are just the easiest to implement, and most of the other options are similar enough to these to fit into the category...
I havenĀ“t tried it with word documents (only with images), but I would try to serve the document directly from php, see my answer about images.
It would be something like an a tag linking to a php page that serves a Word document as its content type.
I know this question is silly.
But as per our intelligent Client request, I am not able to answer his question. Any one help for this.
We are building a online tutoring site. where it contains pdf, .ppt, .doc formats files are uploaded for reading as course materials. His (Client) request is that user can read all the contents but they must not download the materials and use this.
That is all the documents must be opened in their browsers.
Is it possible? Any other ideas?
Any other ideas?
Explain to your client that the only way for a document to appear on a user's computer screen is for the document to exist on that user's computer.
In other words, viewing a document involves downloading it. Even supposing the software on the user's computer somehow makes it impossible for the user to directly manipulate an electronic copy of the material, the user can take out a digital camera and take a picture of the screen.
There are ways to make it difficult for the user to save a copy of the file. However, it's likely that this will do more harm (frustrating users) than good (preventing theft).
Some users may want to peruse the material at times when they do not have an internet connection, or may want to copy it onto their mobile device (for instance), but accessing the internet on their mobile device is expensive so they would like to do the download on their computer.
If you send the data to the client the client has effectively downloaded it. You can make this difficult, but not impossible.
The only sure way to prevent downloading is to prevent viewing.
If this is a copyright problem it should be solved with legalese, not software.
Here are some guide-lines you may consider:
Don't put direct link of files such as:
Download
Instead, try to generate your pdf dynamically or put a another encrypted medium for
downloading eg:
Download
2: Don't allow directory browsing, use htaccess file with following commands:
Deny from ALL
3: Not sure, but you may possibly allow file opening this way too:
$filename="/path/to/file.jpg"; //<-- specify the image file
if(file_exists($filename)){
header('Content-Length: '.filesize($filename])); //<-- sends filesize header
header('Content-Type: image/jpg'); //<-- send mime-type header
header('Content-Disposition: inline; filename="'.$filename.'";'); //<-- sends filename header
readfile($filename); //<--reads and outputs the file onto the output buffer
exit; //and exit
}
Note: above is just an example of image not pdf but you can modify it for your needs.
An online site does not necessarily mean it is a web site. You could write a custom client that accesses the data and displays it.
The data would need to be encrypted between the client and the server. It probably should not be sent 'in bulk' either.
The effort associated with developing that is prohibitive.
You could license the software that allows users to read books, page by page, that is part of the Safari Books Online web site.
As best I can tell, they take the pages that they are going to display and turn them into small images. These images look as if they are sent in a random order, and assembled by the browser via javascript.
These tactics won't stop a determined person from getting your clients content... but the effort is unlikely to be worth it.
You could put the docs into Google docs and embed the docs viewer into your site. Of course, there's no stopping people from taking screenshots, copy/pasting text, downloading HTML, etc.
What do you mean by "read" but not "download"?? Do you know that even if you disable cache (which by itself is a bad idea) won't restrict an eaaaasy right-click>view source, "save target as", etc.?
I mean, the best you can have is a flash reader that is harder to save the content from, and that means disabling selection and copying, but anyway, it doesn't forbid anything.
The only way to forbid download is to return HTTP 403 :)
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.
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.