Jump to content

How to protect files from being downloaded directly via url


hintraeger
 Share

Recommended Posts

I would like to protect files (pdf) that are located in the site/assets/files folder from being downloaded directly via the url that leads to this file. However, when a page containing the file field is called up, it must be possible to make this file available for download.
Is there a solution for this in processwire. The access should be controlled depending on the role of the current user and another parameter.

I tried it like this:

I have protected the folder site/assets/files with .htaccess file:

<FilesMatch "\.(pdf)$">
  <IfModule mod_authz_core.c>
    Require all denied
  </IfModule>
  <IfModule !mod_authz_core.c>
    Order allow,deny
    Deny from all
  </IfModule>
</FilesMatch>
 

Then created a template that makes the file path available for download as follows:

<?php    if( $ok ) {
        header("Content-type:application/pdf");
        header("Content-Disposition:attachment;filename=$downloadname" );
        header("Expires: 0");
        header("Cache-Control: must-revalidate, post-check=0,pre-check=0");
        header("Pragma: public");
        readfile("/var/www/...".$download);
    }

This works perfectly in a test php file, but when integrated via a template file in processwire it does not work.

My guess is that a header is delivered by processwire, a different content-type or something like that.

In the template I have tried contentType application/pdf and empty, neither works.

Link to comment
Share on other sites

sorry i have forgot something. The download works, but the downloaded file is defect: Acrobat Reader reports: An error occurred while opening this document. This file is damaged and cannot be repaired. Outside processwire with the same procedure in pure php the same downloaded file is ok.

Link to comment
Share on other sites

The line with the readfile command has three dots at the end which seems strange to me and then adds whatever is your $download variable. Have you checked that path e.g. via outputin template and checked if file is readable?

Link to comment
Share on other sites

Have you tried the template settings for file-level access control? It might not be exactly what you want (without some fiddling), but it's what currently seems the closest thing to what you're looking for.

image.thumb.png.b3e2239fdbad3c4456b59ca23b7baaa8.png

  • Like 1
Link to comment
Share on other sites

40 minutes ago, cwsoft said:

The line with the readfile command has three dots at the end which seems strange to me and then adds whatever is your $download variable. Have you checked that path e.g. via outputin template and checked if file is readable?

... is just a placeholder, I have removed the correct path. The file with the correct file size is downloaded, but it is broken. But if I download the same file outside of processwire in the same way, then the file is OK.

Link to comment
Share on other sites

8 minutes ago, BrendonKoz said:

Have you tried the template settings for file-level access control? It might not be exactly what you want (without some fiddling), but it's what currently seems the closest thing to what you're looking for.

 

Thanks, yes I tried that too, but unfortunately it's not quite what I need.

Link to comment
Share on other sites

11 hours ago, dynweb said:

Have you checked that there are no files prepended/appended when rendering the template? Maybe disable Tracy debugger?

I have no files prepended/appended and tracy debuger is also disabled. But I know the problem now. The downloaded file is only unreadable on my older Mac by Acrobat, Preview and Safari. In Brave it is and on newer operating system versions there is also no problem. The pdf must have been altered somehow so that it is no longer compatible with older pdf readers. A bit strange. 

But it would be nice if processwire had a solution here on how to prevent the download of files directly via an url link. Maybe there is already such a solution and I just don't know it. I think there was a solution in drupal. There you could decide whether the files should be stored in a directory outside the web directory and thus be protected from direct access. Unfortunately I don't know how the download was solved via a template. It would be great if there was a solution in the future. Many thanks for your answers.

Link to comment
Share on other sites

FWIW -- I have a template called "downloads".

When someone goes to https://example.com/downloads/specialfilename.php, the file name is treated as a url segment. From the url segment, I will essentially:

  • Search for the file
  • Make sure it's there
  • Then deliver it via the mime process
// Get page ($p) where the file lives

// Still here, so lets see if we can get the actual file
$file = $p->filesManager->getFile($fileNameSanitized);
if(!$file) return 'File not found(3)';

header("Content-Type: application/pdf");
header("Content-Disposition: inline; filename=$fileName");
header("Content-Transfer-Encoding: binary");
header("Accept-Ranges: bytes");
@readfile($file->filename);
return true;

 

  • Like 2
Link to comment
Share on other sites

  • 2 weeks later...
On 2/9/2024 at 7:53 AM, gebeer said:

Code here:

and here:

might be helpful.

This implementation is working for me on several projects.

thank you, that is exactly what i searched for

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...