Protect PDF docs from being directly accessed in URL
Asked Answered
I

3

5

I want to protect a pdf file from being directly linked but instead have my logged in users be able to access it. I have a link which currently goes to a javascript function which posts a form: $('nameofdoc').setProperty('value',doc); document.getElementById('sendme').submit();

where sendme is the name of the form and nameof doc the index of the document I want to display.

This then goes to a php file:

 $docpath = $holdingArray[0].$holdingArray[1];

 $file = $holdingArray[0]; //file name
 $filename = $holdingArray[1]; //path to the file]
 header( 'Location:'.$docpath ) ;
 header('Content-type: application/pdf');
 header('Content-Disposition: attachment; filename="'.$filename  . '"');
 readfile($filename)

This all works fine it loads up the file and outputs the pdf. What I can't do is protect the directory from direct linking - ie www.mydomain.com/pathToPdf/pdfname.pdf

I've thought of using .htaccess to protect the directory but it's on a shared host so I'm not sure about the security and anyway when I've tried I can't get it to work.

Any help would be great since this is my fourth day of trying to fix this.

thanks

Update

I've had a lot of help thank you but I'm not quite there yet.

I've got an .htaccess file that now launches another php file when a pdf is requested from the directory:

 RewriteEngine on
 RewriteRule ^(.*).(pdf)$ fileopen.php

When the fileopen.php file lauches it fails to open the pdf

 $path = $_SERVER['REQUEST_URI'];
 $paths = explode('/', $path);
 $lastIndex = count($paths) - 1;
 $fileName = $paths[$lastIndex]; 

 $file = basename($path);

 $filepath = $path;

 if (file_exists($file)) {
     header( 'Location: http://www.mydomain.com'.$path ) ;
     header("Content-type: application/pdf");
     header("Content-Disposition: attachment; filename=".$file);
     readfile($filepath);

 }else{
     echo "file not found using path ".$path." and file is ".$file;
 }

The output is file not found using path /documents/6/Doc1.pdf and file is Doc1.pdf

but the file does exist and is in that direcotry - any ideas??

OKAY I'm happy to report that Jaroslav really helped me sort out the issue. His method works well but it is tricky to get all the directory stuff lined up. In the end I spent a few hours playing about with combinations to get it working but the principle he gave works well. Thanks

Intermeddle answered 1/10, 2013 at 19:16 Comment(8)
What's the code you have tried in .htaccess?Sakmar
TheWolf I was going to use: Order deny,allow Deny from all Allow from 127.0.0.1 but it's a shared host so I think that means anyone on the server would be able to access? Oh yeah and it didn't work :)Intermeddle
There's no need for the Allow from 127.0.0.1 part as only php is trying to access the file which is not affected by .htaccess.Sakmar
Can you move the PDF directory outside of your web root? This is possible on most shared hosts.Mohsen
without the allow from doesn't work either it gives a forbidden messageIntermeddle
I have thought of moving it outside of the webroot but that then give me other problems. I'm storing 30000 pdf docs so I'm using a sharding technique which automatically creates folders for the documents depending on their sha1 encoding. I'm not sure I can use php to create subfolders outside of the webrootIntermeddle
PHP couldn't care less about the webserver's document root. PHP may be running inside the webserver, but it'll have the full run of the entire file system. Put your PDFs outside the webroot and mediate access via PHP scripts. Problem solved.Humpy
So I tried header( 'Location: /var/www/vhosts/mydomain.com/private/Doc2.pdf' ) ; header('Content-type: application/pdf'); header('Content-Disposition: attachment; filename="/var/www/vhosts/mydomain.com/private/Doc2.pdf'); readfile($filename) which didn't work it goes to: mydomain.com/var/www/vhosts/mydomain.com/private/Doc2.pdfIntermeddle
A
9

The best way would be to protect that folder with htaccess, as you have mentioned. So you put all PDFs in pdf/ folder, and in the same pdf folder you out .htaccess file:

RewriteEngine on
RewriteRule .* your-php-script.php

Now no files can be accessed by url in this folder. Every request to every file in this folder will return what your-php-script.php script returns. In your-php-script.php you do something like this:

//Check if user has right to access the file. If no, show access denied and exit the script.
$path = $_SERVER['REQUEST_URI'];
$paths = explode('/', path);
$lastIndex = count($paths) - 1;
$fileName = $paths[$lastIndex]; // Maybe add some code to detect subfolder if you have them
// Check if that file exists, if no show some error message
// Output headers here
readfile($filename);

Now if user opens domain.com/pdf/nsa-secrets.pdf Apache will run your-php-script.php. Script will have variable $_SERVER['REQUEST_URI'] set to "domain.com/pdf/nsa-secrets.pdf". You take the last part (filename) and output it to a user (or not).

This will stop anyone from accessing files directly from the internet by knowing URL. If someone has direct access to files on your server, that will not stop them. On the other hand, I think any shared hosting stops users from getting files of other clients. Only way to do it is to hack the server in some way. But then we are getting very paranoid and if that may be a case for you, you shouldn't use shared hosting in the first place.

If you cannot make htaccess work, you can try to obfuscate files, so it would be difficult to spot them for someone outside. For example change file from mySecretData.pdf to djjsdmdkjeksm.pdf. This may help a little bit.

Assess answered 1/10, 2013 at 19:36 Comment(12)
how would this work. I'm afraid my .htaccess knowledge isn't greatIntermeddle
First line turns on url rewrite engine of Apache (assuming your server uses Apache). The second tells that query for every file in this directory should be internally redirected to your php script, no matter what user called domain.com/pdf/abc.pdf or domain.com/pdf/secret-nsa-presentation.pdf Apache would return your php script.Assess
okay so I had something similar: RewriteEngine on RewriteRule ^(.*).(pdf)$ fileopen.php?file=$1.$2 [L] but I couldn't figure out how to pass variables to the php script through htaccess so it knew which file to openIntermeddle
The reuqest URL is a good idea actually. You could limit access to this folder by domain.Degust
so how would I write the request URI into the htaccess?Intermeddle
In $_SERVER['REQUEST_URI'] variable you will see, that user asked server for a page, for example: domain.com/pdf/hash/nsa.pdf . Just explode this string with '/' [1], look at the file that was requested and either give it to user or show him access denied page. (I'm speaking about my redirect - ".*". [1] php.net/manual/en/function.explode.phpAssess
But that won't overcome the user typing the direct url for the pdf since no php script is run - I thought you meant something like this RewriteCond %{HTTP_REFERER} !^http://([^.]+\.)*www\.mydomain.com\.com RewriteRule ^ redirectfile.php [L]Intermeddle
Jaroslav I tried this and I think it looks promising. It works great referring the file etc but it doesn't seem to refer the domain so I can't find a way to check that it has come from an internal script rather than someone typing it directly into the browserIntermeddle
For this you can use session. Just write $_SESSION['userIsLoggedIn']=true; on login page. In a script that checks where the user comes from look at this variable, if its true, then user has just logged in on another page and can receive his pdf, if it is not set, or set to false you tell him he cannot access this document. You don't need checking domain, because php takes the file directly from file system, no domains involved.Assess
Jaroslav this is kind of working except that it won't let me open the pdf file. Instead it cycles looking for the file. I've tried it with code I know opens the file correctly but it refuses to open itIntermeddle
Jaroslav I meant to include the code $path = $_SERVER['REQUEST_URI']; $paths = explode('/', $path); $lastIndex = count($paths) - 1; $fileName = $paths[$lastIndex]; $file = basename($path); $filepath = $path; if (file_exists($file)) { header( 'Location: church-huddle.com'.$path ) ; header("Content-type: application/pdf"); header("Content-Disposition: attachment; filename=".$file); readfile($filepath); }else{ echo "file not found using path ".$path." and file is ".$file; }Intermeddle
let us continue this discussion in chatIntermeddle
I
1

I want to protect a pdf file from being directly linked but instead have my logged in users be able to access it.

Check to ensure there is an authenticated user before streaming the PDF's content.

Inconsistent answered 1/10, 2013 at 19:18 Comment(2)
This wouldn't stop the direct download of pdf if the URI was know?Intermeddle
@Intermeddle It would, unless the files are hosted above your web root and being served via Apache or something. If that's the case, move them above the webroot or use Apache to prevent itself serving them.Inconsistent
D
0

This is kinda sloppy but it could work assuming you can setup a MYSQL DB. It lets you pass the "password" in the URL as an MD5 string or as a clear text if you want to. Trying to setup some kind of security without using htaccess or an existing frame work is kinda clunky. This however won't even attach the file to the stream until it knows you've been "Authenticated" I think you could maybe make this a little better if you setup a login page that saved a cookie locally then you wouldn't need to pass the "passphrase" in the URL.

    $file = $_GET['file'];
    $pass = $_GET['pass'];
    $download_folder = '../Protected';
    $file = basename($file);
    $filepath = "$download_folder/$file";

    if (file_exists($filepath)) {
        if(CheckUser($pass)){
            header("Content-type: application/octet-stream");
            header("Content-Disposition: attachment; filename=$file");
            session_write_close();
            readfile($filepath);
        } else {
            echo 'Not Authenticated!';
        }
    } else {
        echo 'No File!';
    }

function CheckUser($value){
    $con = mysqli_connect("test.com","test","123456","my_db");
    // Check connection
    if (mysqli_connect_errno()){
        echo "Failed to connect to MySQL: " . mysqli_connect_error();
    }
    $result = mysqli_query($con,"SELECT user FROM pass_table WHERE password =".md5($value).";");
    while($row = mysqli_fetch_array($result)){
        mysqli_close($con);
       //return $row['user'];
       if($row['user']){
           return true;
       }
    }
    mysqli_close($con);
    return false;

}
Degust answered 1/10, 2013 at 19:43 Comment(1)
I thought of something like this but whilst it would protect to some extent if the person knew the direct URL for the PDF then there wouldn't ever be a need to run the script so the password authentication wouldn't helpIntermeddle

© 2022 - 2025 — McMap. All rights reserved.