PHP File Download with Query String - php

I am curious how I could make a PHP script where you pass your username and password to my webserver and it downloads a file (or multiple) that is specific to that one user only. I am not sure where to start. Could someone please help? thanks!

Here it is:
http://www.php.net/manual/en/function.readfile.php
You shouldn't put a password directly in the URL. Instead, use a random generated token, for example using MD5 ($token = md5($username.time());). So, your user table should look like this :
id
email
password
token
The URLs you'll provide to your user will be like this :
http://www.example.com/download.php?token=$userToken&file=$file
And download.php:
<?php
if ( ! isset($_GET['token']) || ! isset($_GET['file'])) {
//throw 404
}
//check token exists in DB else throw 403
//use readfile function :)

First you could create a mysql table for all users. Then you could make a second one where the filenames and usernames are saved like "file:test.png user:xyz" that every file has an owner. If somebody wants to download a file you have to check if this user is the file owner in the database. The list of all files then shows all files of this user.

Here's a simple example:
download.php
<?php
if (isset($_GET['user_info']) && check_validity($_GET['user_info'])) {
$file_info = determine_file_info($_GET['user_info']);
header("Content-type: " . $file_info['type']);
header('Content-disposition: attachment; filename="' . $file_info['name'] . '"');
readfile($file_info['absolute_path']);
exit;
}
?>
That mock-up imagines you have defined a function check_validity that determines if the given user info is valid, and that you have defined a function determine_file_info that uses the user info to build an array of file type, file name, and file path (where the file is on the server).

Related

Changing file permissions in php

I'm a sysadmin for a small firm and I manage the server for them.
I've setup a portal for our customers to view their bills in pdf format, they are initially set with 0600 file permissions. For security reasons I cannot have all the pdf's 'visible' to everyone so I need a way to show them to the customer only when a pdf is clicked on the customers' account.
I have tried using the following, but it doesn't work and I'm getting a 'Forbidden' error...
chmod($filename, 0755);
echo "<td><iframe src='" . $filename . "' width=645 height=600 frameborder=0></iframe></td>";
chmod($filename, 0600);
The php script and the pdf files have the same owner set.
Any ideas what I'm doing wrong, I need to get this working?!
Many thanks! :)
This can not possibly work:
chmod($filename, 0755);
echo "<td><iframe src='" . $filename . "' width=645 height=600 frameborder=0></iframe></td>";
chmod($filename, 0600);
You're making the file readable only for the amount of time it takes PHP to echo that one line of HTML. I.e., by the time the user clicks the link, permissions have already been revoked again. On top of that, the file is world-readable for that period of time, so anybody on the Internet can see it.
To do this more securely, do not have the web server serve the files directly, as you will not be able to control who has access to them. Instead, put them outside the document root so that they can not be seen at all by the web server, and then proxy them through a PHP script (via readfile() or similar) that performs an ownership check.
In your script that generates the link:
echo 'PDF Download';
Where $fileId is some unique identifier for the file, but not the full file name.
Then, in download.php, something like this:
function getLoggedInUser() {
// return the logged-in user
}
function getFileForId($fileId) {
// get the full path to the file referenced by $fileId
}
function getOwnerOfFile($fileId) {
// get the user allowed to see the file referenced by $fileId
}
$fileId = $_GET['fileId'];
$file = getFileForId($fileId);
if (!file_exists($file)) {
header('HTTP/1.1 404 Not Found');
exit;
}
if (getLoggedInUser() !== getOwnerOfFile($fileId)) {
header('HTTP/1.1 403 Forbidden');
exit;
}
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="whatever.pdf"');
readfile($file);
[UPDATE]
and I have <a href="/viewbill.php?bid=<?php echo $invoice_number; ?>" title="View PDF Invoice"> where the $invoice_number is the name of the file.
That's fine, just make sure that viewbill.php performs a check to ensure that the logged-in user is the same as the user that the bill is for, otherwise any customer can view any other customer's bills simply by changing the invoice number in the URL.
When you say 'put them outside the document root' where do you mean exactly;
Let's say that your Apache document_root directive points to /var/htdocs/public/. In this case, everything in that directory and everything under it can be seen by Apache and potentially served directly to a client. E.g., if you have a PDF file in /var/htdocs/public/pdfs/12345.pdf then a user can simply request the URL /pdfs/12345.pdf in their browser, regardless of what PHP structures are in place. Often this is mitigated with the use of .htaccess files but this is not ideal. So, if you have files that you want to keep controlled, you should not put them anywhere under the document_root. For example, put them in /var/htdocs/docs/ instead. This way, Apache can not possibly see them, but you can still use readfile() to pull their contents.

Allow access to particular file in PHP

In my project, user will upload a pdf file. That will be stored in a directory named 'uploads' with name $_SESSION['userId'].".pdf". Now user can access the file using 'myweb.com/uploads/id.pdf'. But when the user change id value in url bar of browser he is able to access other user's file. To prevent it, istead od a link I would like to use a form to post $_SESSION['userId'] with hidden type to next page. In the next page will be redirected to the file using the posted id.
Is this idea okay? Or are there any better solutions?
In to the folder of PDF files you have to create .htaccess file and place following two lines in it.
Order Deny,Allow
Deny from all
This .htaccess file will not allow to access any PDF file directly from the browser.
To allow to access PDF file to the logged in user create a PHP file downloadpdf.php and place following code in it. The logged in user will able to download his/her file only this way.
<?php
$pdf_file = "{$_SERVER['DOCUMENT_ROOT']}/path-to-pdf-file/".$_SESSION['userId']".pdf";
if( file_exists( $pdf_file ) )
{
header( 'Cache-Control: public' );
header( 'Content-Description: File Transfer' );
header( "Content-Disposition: attachment; filename={$pdf_file}" );
header( 'Content-Type: application/pdf' );
header( 'Content-Transfer-Encoding: binary' );
readfile( $pdf_file );
exit;
}
die( "ERROR: invalid song or you don't have permissions to download it." );
?>
If you don't want user to see the files of other user then you never expect result from the user from client side.
Store the data in session/database something and retrieve the value from it in case of restricted usage.
Is it really necessary to assign id to the file name? Using id as a hidden field is still very much vulnerable.
Why don't you generate a filename with a unique ID for every user? You may add one more database field called uuid (char 36), if necessary. You can use uniqid() functino.
So, every time, instead of checking with the primary key id, you can store uuid in session and check accordingly.
Better solution is Check isset $_SESSION['userId'] and compare that with the id in url
<?php
session_start();
$link = $_SERVER['PHP_SELF'];
$link_array = explode('/',$link);
echo $page = end($link_array);
$id = explode('.',$page);
$userid = $id[0];
if(isset($_SESSION['userId']) && $userid== $_SESSION['userId']){
// display file
}
else{
//not authorized
}
?>

How to hide download file path from user using php?

When user want to download file from my web site, user have to click link like below
https://www.example.com/download.php?aaa=111&bbb=222
download.php
<?PHP
session_start();
include("connect.php");
$aaa = mysql_real_escape_string($_GET[aaa]);
$bbb = mysql_real_escape_string($_GET[bbb]);
if(($aaa = '111')&($bbb = '222')) // this line is example for ask stackoverflow //
{
$filePath_try_to_download = 'attachments_files/test.pdf';
if(file_exists($filePath_try_to_download))
{
$fileSize = filesize($filePath_try_to_download);
$fileName = "test.pdf";
header("Cache-Control: private");
header("Content-Type: application/stream");
header("Content-Length: ".$fileSize);
header("Content-Disposition: attachment; filename=".$fileName);
// Output file.
readfile ($filePath_try_to_download);
exit();
}
}
?>
I want to know when user download file from this link https://www.example.com/download.php?aaa=111&bbb=222 user can get my file path on server or not (attachments_files/test.pdf). If user can get my file path, how can i hide it's ? (file in this dir is very importance)
Since I was posting comments from my Phone, they couldn't really explain much, so here goes your answer.
I want to know when user download file from this link https://www.example.com/download.php?aaa=111&bbb=222 user can get my file path on server or not (attachments_files/test.pdf).
No, Users can not see that file path which you are reading via readfile(). They will not be able to find out that file's location at all.
And if you want to eliminate any chances of people guessing the file path simply put those files outside of your web root folder and then readfile() them from there.
$filePath_try_to_download = 'attachments_files/test.pdf';
That path is only known to your PHP code, which is not visible to users hence they have no idea from where did you read the file they are downloading, just eliminate the guesswork chances though :)
And Obviously you have to secure access to this url https://www.example.com/download.php?aaa=111&bbb=222 otherwise what's the point!
No. The user cannot get the file path. He only get the content outputed by PHP script.
Your can do this and the user only get the "Hello" string. So it's your PHP script's role determining which contents the user can get.
<?php
echo "Hello";
?>

Generating unique download link to download once only

I wanna create a few unique download link for my users. The reason is that I wanted to let them download once only, so that they can use back the same link to download again.
I've generate a few of the keys (example, qwertyasdfghzxcbn. As in the download link will be like www.xxxxx.com/download.php?qwertyasdfghzxcbn) in the database and flag field where when the user downloaded, it will update 1 to the flag field.
I did a search on the net and found this.
http://www.webvamp.co.uk/blog/coding/creating-one-time-download-links/
But that only works when you go to the page first then only the page will generate the unique link. I've already pre-generate the link inside my database, I don't need to regenerate again, if fact if I generate the key when user go the page, they will able to download multiple times by refreshing the page.
The solution would be to make the link target itself a PHP script.
You'd hide the actual file somewhere inaccessible from the browser (i.e., somewhere where you can reach the file via fopen(), but isn't within the document root), and put a download.php file to download files.
The download script itself would look something like this:
$fileid = $_REQUEST['file'];
$file = file_location($fileid); // you'd write this function somehow
if ($file === null) die("The file doesn't exist");
$allowed = check_permissions_for($file, $fileid) // again, write this
// the previous line would allow you to implement arbitrary checks on the file
if ($allowed) {
mark_downloaded($fileid, $file); // so you mark it as downloaded if it's single-use
header("Content-Type: application/octet-stream"); // downloadable file
echo file_get_contents($file);
return 0; // running a return 0; from outside any function ends the script
} else
die("You're not allowed to download this file");
Any link you point would simply point to download.php?fileid=712984 (whatever the fileid actually is). That would be the actual download link, since that script does transfer the file; but only if the user is allowed to retrieve it. You'd have to write the file_location(), check_permissions_for() and mark_downloaded() functions yourself though.
I would suggest using uniqid() function, and store unique ids with the expiration date in a database, while returning to the user url with something like this: ...?file_id=$id
When the link is being opened, you may delete it from the database or mark it to be deleted 'soon' (just in case user wants to refresh the page.)

allowing only a specific user to download files off the server

I'm creating files based on user data (taking from the database table, writing to a csv file, storing on the server, and then printing out a link for the user (logged in) to download).
how can I ensure that only the user can download that file? For instance, if the file is stored at http://mysite.com/username/file, how can I make sure only that user, when signed in, is the only one who can download the file? Using php.
thanks for the help
The PHP manual has a user submitted entry that does what you're looking for:
function query_to_csv($db_conn, $query, $filename, $attachment = false, $headers = true) {
if($attachment) {
// send response headers to the browser
header( 'Content-Type: text/csv' );
header( 'Content-Disposition: attachment;filename='.$filename);
$fp = fopen('php://output', 'w');
} else {
$fp = fopen($filename, 'w');
}
$result = mysql_query($query, $db_conn) or die( mysql_error( $db_conn ) );
if($headers) {
// output header row (if at least one row exists)
$row = mysql_fetch_assoc($result);
if($row) {
fputcsv($fp, array_keys($row));
// reset pointer back to beginning
mysql_data_seek($result, 0);
}
}
while($row = mysql_fetch_assoc($result)) {
fputcsv($fp, $row);
}
fclose($fp);
}
// Using the function
$sql = "SELECT * FROM table";
// $db_conn should be a valid db handle
// output as an attachment
query_to_csv($db_conn, $sql, "test.csv", true);
// output to file system
query_to_csv($db_conn, $sql, "test.csv", false);
I was about to edit your question and remove the sql and csv tags because, as asked, it doesn't really have to do with those topics. However, my answer does.
Instead of saving a file to the file system, just generate the csv file for output directly to the response. Your code would only generate the correct csv for the user (or no content for non users), thereby solving the problem.
This assumes generation of the file is modestly lightweight and would not be done very frequently.
You could make a file called download.php, for example:
if($logged_in && $username == $_GET['download_username']) {
header("Content-Type: application/force-download");
header('Content-Disposition: attachment; filename=nameofdownload.zip');
readfile('downloads/' . $_GET['download_username'] . '/file.zip');
}
Then in the downloads folder, you could create a .htaccess file, and put the following:
Deny from all
If the user is not logged in, or if it's a wrong user, just show a message or redirect or whatever you want instead of giving access to the file. And that url example you've shown should be only a page with a link or redirection to a real file. You can even generate a file name hashes for even better security.
Unfortunatley you will need to wrap the download in some code. You can use a technique similar to this:
if ($user->is_correct_user_with_access())
{
header('Content-Type: application/octet-stream'); // this can be replaced with whatever file format you want;
echo file_get_contents($local_path_to_file);
die;
}
Also ensure that the file you are generating is not in a path that is publicly accessible (ie, it should be below your document root)
Sounds like a trivial question... there's any number of ways, if the user is logged in then simply check the file they're requesting belongs to them.
The best way to do this would probably be to use a pass-through file. For example the request to /username/file would actually be calling /download.php?id=username&dl=file in which you can then checked the logged in user is equal to id or the username of the file trying to be downloaded from and if they match then use php header location to redirect the request to another location although that can be exploited by looking at headers; so to make it seemless, you'd store the files outside the web directory and read in the file using php and output it directly to the browser.

Categories