3

I am trying to protect files on my server (multiple types), with NGiNX and PHP.

Basically I want people to have to sign in to the website if they want to access those static files like images. DropBox does it very well. Where by they force you to sign in to access any static files you put on there server.

I though about using NGiNX Perl Module. And I would write a perl script that would check the session to see if the user was sign in to give them access to a static file.

I would prefer using PHP because all my code is running under PHP and I am not sure how to check a session created by PHP with PERL.

So basically my question is: How can I protect static files of any types that would need the user to have sign in and have a valid session created with a PHP script?

user9517
  • 115,471
  • 20
  • 215
  • 297
jnbdz
  • 927
  • 5
  • 24
  • 46
  • 1
    I would direct you to [SO], but it sounds like you want an intro to PHP Session management and programming. Picking up a book on PHP or Googling for it should yield many results. – Chris S Nov 19 '11 at 03:28
  • My question is not related to PHP Session. That I can figure it out by myself... I am looking for the best way to protect files on my NGiNX server and force users to sign in if there not. – jnbdz Nov 19 '11 at 03:33
  • 2
    Any files that are above the web root can be accessible by PHP but not directly in a browser. As such, have your PHP script upload files one level above your web root, so that they cannot be directly accessed. Use PHP for logins/authentication, etc. When a user requests a file, use the 'X-Accel-Redirect' header (with sendfile in nginx) to directly deliver the file (any application logic - i.e. checking if the user should have permission to access the file, etc - would be done on the PHP side). – cyberx86 Nov 19 '11 at 03:46
  • Isn't there a danger that a user can use the page with the 'X-Accel-Redirect' header and then get the file without sign in? – jnbdz Nov 19 '11 at 04:02
  • Yep, the PHP script would have to check for that.... This ain't rocket science, you can't ever trust user input, it has to be sanitized, sanity checked, and app-logic checked before it's acted upon. If you don't know the basics you really do need to start with an Intro Book or similar learning material. – Chris S Nov 19 '11 at 04:29
  • The part I am trying to understand is what happens when the server receives the 'X-Accel-Redirect' header. Does NGiNX call a php script so I can validate the 'X-Accel-Redirect' header so that no one as change it's value? – jnbdz Nov 19 '11 at 04:46
  • The X-Accel-Redirect header is simply telling nginx to serve the file specified in the header. The way you secure that location is to make the location `internal` (so that no external access is permitted) or to put it above the document root (again, preventing external access). The path provided in the header is not a valid URL - it is an internal path that is not accessible from the outside. If you want to make this somewhat transparent to the user, setup a rewrite that points a 'directory' (e.g. files) to your php script. See: http://kovyrin.net/2006/11/01/nginx-x-accel-redirect-php-rails/ – cyberx86 Nov 19 '11 at 05:54

1 Answers1

14

There are two things you are asking:

  1. How to prevent external access to a (set of) file(s)
  2. How to authenticate a user and allow PHP (and the user) access to the file(s)

Hopefully this clarifies the role each component plays - although it may be a bit excessive.

Prevent External Access to files:

This part is done by your web server - in this case nginx.

In your nginx configuration (your server block), you specify a root path. By default all files under this root path are directly accessible.

For instance, consider the following (the domain is 'example.com')

root /var/www/example.com/public_html

If you have a file, uploaded_file.zip, it will be accessible in the following ways:

  • Location: /var/www/example.com/public_html/uploaded_file.zip - accessible via example.com/uploaded_file.zip
  • Location: /var/www/example.com/uploaded_file.zip - not accessible (even using example.com/../uploaded_file.zip)

In essence, a file above the document root, will not be accessible via a browser, however, most configurations of PHP will allow your code to read the file, and deliver it (restrictions on this are often a result of open_basedir)

In some scenarios, it may be preferable to have your files under the document root. To do this and still prevent (direct) external access, you will use the internal directive. For instance:

location /uploads { internal; }

Now, any files placed in /var/www/example.com/public_html/uploads are only internally accessible (i.e. cannot be accessed via a browser, but can be accessed by your PHP scripts). If desired, you can also setup an alias on the /uploads location, so that another path can also be used to reference it.

At this point, your files are not directly accessible, but your scripts can still be able to serve them.

User Authentication and PHP:

Consider the following basic design: A user can access a file directly, or can go the login page. If they try to access a file directly and are logged in (and have permission), the file will download, otherwise, they will end up on the login screen. To upload a file, a user needs to be logged in.

Let's say that your download script (the page that will deliver the file to the user) is called 'download.php'. A typical URL may be example.com/download.php?file=uploaded_file.zip. It is quite likely that you don't want your URLs to include the php filename. To avoid this, you can setup a rewrite rule in your nginx config that will point another location (let's say /files) at your script. Add to your nginx config:

rewrite ^/files/(.*) /download.php?file=$1 last;

(Which amounts to changing the requested path of any requests starting with /files, and capturing everything after the / and passing it as a query parameter to your php file);

Now, your file can be accessed via: example.com/files/uploaded_file.zip (which internally redirects to your php script).

Your PHP file (download.php) now does the following:

  • Start the session
  • If the user is logged in, proceed, otherwise, redirect to a login page, passing the target file as part of the query string
  • Query your database (e.g. MySQL) to see if the user can access this file
    • If the user has permission, proceed, otherwise, redirect to an error page
  • Output the necessary headers (Content-Type, Content-Disposition, etc) and set:

    X-Accel-Redirect: /uploads/uploaded_file.zip;

Note, you can do the actual download fully from PHP (e.g. using readfile)- it is just a bit more efficient, especially for large files) to do it via nginx. Also, despite the user appearing to access the file directly, internally, it is going through PHP, which is authenticating them before allowing them to continue.

URL -> nginx (redirect to PHP) -> PHP (authenticate) -> nginx (serve file)

On the uploading side of things, you will move_uploaded_file to /var/www/example.com/public_html/uploads (which is protected by the 'internal' directive, and so, files cannot be directly accessed), and save permissions to your database.

cyberx86
  • 20,805
  • 1
  • 62
  • 81