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
}
?>
Related
I have a site for articles applications.
I need to deny all direct access to these files but allow the download on certain pages. These files are .pdf
The location of the files is:
"./public_html/files"
Is there a way to deny all direct access to these files, but allow the download from specific buttons?
You must implement a wrapper on top of your file system and add extra security for this purpose like token.
Let's say your route for downloading is:
<BASE_URL>/download/{x}
and x is name of the file. This route will only return the file content from ./public_html/files if user provided a valid token in url like:
<BASE_URL>/download/{x}?token=diji12jidj
All of the implementation of this method depends on your needs, e.g. token can be based on user session or based on time or ...
Here is a simple hint in php:
// First page
session_start();
$_SESSION['download_key'] = random_string() // a function that you can implement in anyway you want
// In you html
Download here
// File: download.php
if(isset($_SESSION['download_key']) && $_REQUEST['download_key'] == $_SESSION['download_key']) {
// find file
$path = ... // path to file
$fp = fopen($path, 'rb');
// send the right headers
header("Content-Type: image/png"); // or anything else
header("Content-Length: " . filesize($path));
fpassthru($fp);
exit;
}
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).
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.
I have a website where users should be able to log in and listen to a song (a self-created mp3). I want to make it so the logged in user can listen/download/whatever, and the file should reside on the server (not be stored in the MySQL database), but not be able to be accessed by non-users who have the path to the URL.
For example: say my mp3 is located at mysite.com/members/song.mp3 If you are logged in, you should be able to see the mysite.com/members/index.php page, which will allow access to the song.mp3 file. If you're not logged in, the mysite.com/members/index.php page will not show you the song.mp3 file, and linking directly to it should not grant access.
I'm pretty sure this is done via htaccess, and I have done a lot of Googling already, and searched on here. The two closest answers I found were this htaccess guide http://perishablepress.com/press/2006/01/10/stupid-htaccess-tricks/ and this StackOverflow question Block direct access to a file over http but allow php script access but neither answer all my questions to meet my criteria. What am I missing?
Into folder members create new folder files, move here all your songs, create new .htaccess file and add the following lines:
Order Deny,Allow
Deny from all
Into folder members create file get_song.php and add the following code:
if( !empty( $_GET['name'] ) )
{
// check if user is logged
if( is_logged() )
{
$song_name = preg_replace( '#[^-\w]#', '', $_GET['name'] );
$song_file = "{$_SERVER['DOCUMENT_ROOT']}/members/files/{$song_name}.mp3";
if( file_exists( $song_file ) )
{
header( 'Cache-Control: public' );
header( 'Content-Description: File Transfer' );
header( "Content-Disposition: attachment; filename={$song_file}" );
header( 'Content-Type: application/mp3' );
header( 'Content-Transfer-Encoding: binary' );
readfile( $song_file );
exit;
}
}
}
die( "ERROR: invalid song or you don't have permissions to download it." );
And now, you can use this URL to get the song file:
http://mysite.com/members/get_song.php?name=my-song-name
The only thing you can do for this via .htaccess is require a referer that comes from your site, and it is NOT secure. it is beyond trivial to forge a referer and anyone could suck your site dry.
The ONLY way you'll be able to have only logged-in users download the file is by placing the file OUTSIDE of your webroot and having a PHP script mediate access. In short:
if (is_logged_in()) {
readfile($name_of_file);
} else {
die("Access denied");
}
Are you using a scripting language such as PHP to handle your website? if so then the best way is to create a script that handles "delivery" of the content. Save the content in a protected directory, ie above your http or www folder. Then when the user is logged in, the link to your content would look like this:
http://yoursite.com/listen.php?song_id=xxx
the script will locate the required song by the id and then present the data to the user
Noobie question: I have sensitive content (images) that I want to place in a directory and show a user only AFTER they've logged in. What is the standard procedure for this without relying on simply randomly generated filenames and a .htaccess file that specifies no directory listing? I'm working in PHP.
Thanks!
You can put these images in a different folder outside of the public_html (so nobody can access them). Then via script, if a user is logged in, you get the image file content and then change the header. If a user is not logged, you can display a random image or showing a default image.
for example, the public html folder is: /var/www
your image folder can be: /registered_user/images/
Then in your PHP script you can write:
<?php
if(!userLogged() || !isset($_GET['image'])) {
header('Location: /');
die();
}
$path = '/registered_user/images/';
$file = clean($_GET['image']); // you can create a clean function that only get valid character for files
$filename = $path . $file;
if(!file_exists($filename)) {
$filename = '/var/www/images/bogus.jpg';
}
$imageInfo = getimagesize($filename);
header ('Content-length: ' . filesize($filename));
header ('Content-type: ' . $imageInfo['mime']);
readfile ($filename);
Then when you call the image you can use: <img src="/script.php?image=filename">