NotionCommotion Posted March 15, 2015 Share Posted March 15, 2015 I have an application where the user can upload and download files. To do either, the user needs to be logged on with their username credentials. For downloads, I include a link such as the following, and use PHP to first authenticate and then use either PHP's readfile() or Apache's X-Sendfile to download. <a target="_blank" href="index.php?task=dispDoc&id=516789792">myfile.xlsx</a> Now, I have a certain page where I display the user's deleted documents. The documents are not really deleted, but just have deleted status as stored in the database. For these, I could easily include a link such as the following, and make displayDeletedDoc do what needs to be done. <a target="_blank" href="index.php?task=dispDeletedDoc&id=516789792">myfile.xlsx</a> The problem with approach is I don't want them to be able to be downloaded from anywhere but this single page. Any recommendations? Quote Link to comment Share on other sites More sharing options...
iarp Posted March 15, 2015 Share Posted March 15, 2015 (edited) On your download page check for the $_SERVER['HTTP_REFERER'] and see if it matches your site and the deleted documents page name, otherwise redirect them. The referer is spoofable though, so anyone that is smart enough to try it could potentially download the file from elsewhere. Another idea that came to mind, generate random ids in the users session data that maps to the real id, regenerate those ID's on every page refresh. That way they would need the latest ID's, the modified referer and could only grab it once since the other ID's would be regenerated again. This could cause headaches for legitimate people. Edited March 15, 2015 by iarp Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted March 15, 2015 Author Share Posted March 15, 2015 Thanks iarp, Went with the session idea. Why do you think a headache for legitimate users? Page is displayed in one window, user opens another page in another window, and then clicks link on first page? Quote Link to comment Share on other sites More sharing options...
kicken Posted March 15, 2015 Share Posted March 15, 2015 Page is displayed in one window, user opens another page in another window, and then clicks link on first page? That's one possibility that would cause issues if you're constantly re-generating the random ID's. There are plenty of other ways someone might end up with an invalid ID without realizing why (or even being the cause). For example if someone were downloading using a download manager, there may be two requests for the file: One by the browser which is aborted when it determines the content to be a download, and another later by the download manager. If the browser or an extension uses pre-fetching, it may start requesting other pages on your site in the background in order to speed up the user's browsing experience (chrome will do this if you ask it to). Each of these requests would re-generate the ID's and result in invalid links. The least troublesome method is to generate the IDs as needed, and time-limit them rather than usage-limit them. You could generate the tokens when creating the link, or when they initially request the download, either way. For example: Create the initial link with the actual database ID or something else that doesn't change. Link to some intermediate file rather than directly to the download link <a target="_blank" href="retrieveFile.php?id=516789792">myfile.xlsx</a> In retrieveFile.php generate the random token and store it in the session along with the initial request time and file ID. Redirect to another file with the token. $token = generateRandomToken(); $_SESSION['downloadTokens'][$token] = array( 'initiated' => time() , 'id' => $_GET['id'] ); header('Location: download.php?token='.$token); Verify the token and download the file. define('GRACE_TIME', 3600); //Token is valid for an hour, adjust as desired. $details = isset($_SESSION['downloadTokens'][$_GET['token']])?$_SESSION['downloadTokens'][$_GET['token']]:null; if (!$details || time()-$details['initiated'] > GRACE_TIME){ header('HTTP/1.0 404 Not Found'); header('Status: 404'); echo 'Token not valid.'; exit; } //download the file. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted March 16, 2015 Author Share Posted March 16, 2015 (edited) For example if someone were downloading using a download manager, there may be two requests for the file: One by the browser which is aborted when it determines the content to be a download, and another later by the download manager. If the browser or an extension uses pre-fetching, it may start requesting other pages on your site in the background in order to speed up the user's browsing experience (chrome will do this if you ask it to). Each of these requests would re-generate the ID's and result in invalid links. I didn't even contemplate those scenarios. Thank you for pointing them out. I haven't yet reviewed your solution in detail, however, the concept makes sense. I will spend some time and post addition questions if needed. On a side note, I see how you assigned "_blank" as the target attribute for the download file link. I've been pondering doing so for a while, and recently updated my code to do so. I, however, experienced some negative results when the links were presented in a JavaScript generated dialog, and reverted to not doing so. The only reason I bring it up is that I will post a more on-target new thread on this subject and hope to get your advise (however, if you have some immediate advice, please don't hesitate to voice it!). Thanks again Edited March 16, 2015 by NotionCommotion Quote Link to comment Share on other sites More sharing options...
kicken Posted March 16, 2015 Share Posted March 16, 2015 On a side note, I see how you assigned "_blank" as the target attribute for the download file link. I just copy/pasted your link from the original post and edited the href value. I generally do not add a target to any of my links. For items I think should be in a popup I'll just give them a class="popup" or similar and have a bit of jQuery that attaches to it and opens it into a new window. Otherwise it's up to the end-user to open it in a new tab/window if they want too using their browser. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted March 16, 2015 Author Share Posted March 16, 2015 Thanks Kicken. You saved me a new post!I read (http://www.searchenginejournal.com/when-not-to-use-target_blank-link-attribute/19924/) how when downloading pdf/etc files, the target could improve the ux. The cases when the attribute can (and is encouraged to) be used are the following:to link to a PDF document mostly because when it’s first followed there’s often a delay while the browser loads up the plugin and PDF (and thus you can let the visitor read more information on the current page while the document loads). Caused issues for some circumstances, so not sure it was good advise and don't plan on following. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.