• I’m trying to protect files within a given directory from direct access

    I have tried numerous combinations of contents in different .htaccess files

    order deny,allow
    deny from all

    I have developed a website which has a structure that can be summarised as:

     htdocs
         .htaccess
         ...
         wp-content
          ...
          uploads
           ...
           Private
            .htaccess
            ...

    I need to access files in the Private directory from a Members Only page within the website, but protect them from direct access. For example, if I enter the following in a web browser I can access a document directly:
    http://www.Mywebsite.com/wp-content/uploads/Private/Admin-Accounts.xls
    I thought this could be solved by including the following in the Private/.htaccess file:

    order deny,allow
    deny from all

    However, it doesn’t seem to matter what is in the Private/.htaccess file, I can still directly access files in the Private directory. The contents of the .htaccess file in the root directory seem to over-rule. If I search the site I find .htaccess files in numerous directories. What defines which one is in use?

    As a supplementary question. if I could get the Private/.htaccess to be recognised, I suspect that in its current form it would block all access, including access from the Members Only page. How can I modify it to allow access from within the website but block it from access from any other location?

    • This topic was modified 6 years, 10 months ago by RouseA.
    • This topic was modified 6 years, 10 months ago by RouseA.
Viewing 6 replies - 1 through 6 (of 6 total)
  • Dion

    (@diondesigns)

    Your first question cannot be answered because you haven’t provided enough information. What is your PHP handler? What are the contents of your main .htaccess file? What version of Apache are you using?

    Your second questions is easier…it’s not possible to reliably do what you want with direct URL access to files. File access should go through a PHP script that can check whether the user has permission to access the file. (I used reliably above because some people will tell you to check referrer headers. Referrer headers can be easily spoofed and are NOT reliable.)

    Thread Starter RouseA

    (@rousea)

    I should start by saying that I am by no means expert in these matters, but I am keen to learn!

    As I understand things, the PHP version and Apache are dictated by the company used to host the website. My host uses PHP7.3, but I can’t identify anything about Apache. Any suggestions how I can find more details?

    My main .htaccess file was created by WordPress, and the contents are as follows:

    # BEGIN WordPress
    <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteRule ^index\.php$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]
    </IfModule>
    # END WordPress
    # Wordfence WAF
    # END Wordfence WAF

    I have tried expanding this, which proves that it is active (I was able to block all web access until I restored the contents via FTP).

    However, your response to the second part of my query suggests that I am following the wrong route in assuming privacy should be provided via .htaccess. Can you give any guidance on how I could achieve the desired objective using PHP?

    • This reply was modified 6 years, 10 months ago by RouseA.
    • This reply was modified 6 years, 10 months ago by RouseA.
    • This reply was modified 6 years, 10 months ago by RouseA.

    You could use something like the following in the site’s root .htaccess file (one created by WordPress):

    # BEGIN file lock-downs
    <ifModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{SCRIPT_FILENAME} /wp-content/uploads/Private/.+\.xls [NC]  
    RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_  
    RewriteRule ^ - [F]
    </ifModule>
    # END file lock-downs

    What it does is require a visitor to be logged into the site in order to be able to access that directory. Of course it can be bypassed if someone knows you’re doing that and they know how to spoof a cookie (which isn’t hard to do).

    I haven’t actually tested that, so if that doesn’t work, you might try changing the “SCRIPT_FILENAME” to “REQUEST_FILENAME”

    So now it looks like:

    # BEGIN file lock-downs
    <ifModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} /wp-content/uploads/Private/.+\.xls [NC]  
    RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_  
    RewriteRule ^ - [F]
    </ifModule>
    # END file lock-downs

    But that may help get you down the route you’re trying to achieve. You could actually use it in combination with other directives and not just cookies.

    I need to mention that I tried to provide something within .htaccess that might do what you are trying to achieve with just .htaccess. As someone else mentioned, it would be better to do a combination check with .htaccess and a PHP script as that would be more secure (harder to spoof). But until then, that .htaccess snippet, might get you started.

    • This reply was modified 6 years, 8 months ago by Andrew Nevins.
    Thread Starter RouseA

    (@rousea)

    Thanks for your suggestions.

    I tried replacing the root .htaccess with your suggestion, but I was then unable to open any sub-pages (presumably because it was blocking access to the index and/or directory). I added the WordPress .htaccess so I was able to open sub-pages, thus my root .htaccess became

    <IfModule mod_rewrite.c>
    RewriteEngine On
    
    # BEGIN WordPress
    RewriteBase /
    RewriteRule ^index\.php$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]
    # END WordPress
    
    # BEGIN file lock-downs
    # RewriteCond %{SCRIPT_FILENAME} /wp-content/uploads/Private/.+\.xls [NC]
    RewriteCond %{REQUEST_FILENAME} /wp-content/uploads/Private/.+\.xls [NC]  
    RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_  
    RewriteRule ^ - [F]
    # END file lock-downs
    
    </ifModule>

    However, I was then unable open private files from within the website (or directly).

    Thomas is obviously aware of the syntax of .htaccess files, so I hope he can suggest a further improvement. I would like to learn the syntax and would appreciate guidance on where to find instructions.

    Okay, I had a bit of time to test this today on one of my test sites. And this is what worked.

    I created a directory in my /wp-content/uploads/ directory named /private/

    And I created an .xls file named test.xls and uploaded it to that /private/ directory.

    I also create an .htaccess file with the following contents:

    # BEGIN file lock-downs
    <ifModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{SCRIPT_FILENAME} .+\.xls [NC]  
    RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_  
    RewriteRule ^ - [F]
    </ifModule>
    # END file lock-downs

    And I also placed it in the /private/ directory along with the test.xls file.

    Since I was already logged into this WordPress site, I went directly to the test.xls file in that directory:
    https://example.com/wp-content/uploads/private/test.xls

    And it let me download the file.

    I then opened up a new private window (incognito in Google Chrome – or you could just try to open it up in a new browser where you haven’t logged into the site). And tried again, but this time it presented me with a 403 Forbidden page. So the above worked as expected on my test site.

    If that presents a problem with your site, it’s possible there is an issue/conflict somewhere else. So you might try removing the ifModules as they are just there for portability. The ifModules just tell the server if that module is not activated or doesn’t exist, please die silently.

    # BEGIN file lock-downs
    RewriteEngine On
    RewriteCond %{SCRIPT_FILENAME} .+\.xls [NC]  
    RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_  
    RewriteRule ^ - [F]
    # END file lock-downs

    Also, please remember this separate .htaccess file needs to go in the directory with the files that you are trying to restrict access to.

    If your server allows using .htaccess files in directories further from the root, they should override what’s in the root .htaccess file.

    Also, I need to mention, you don’t want to do it like this:

    RewriteCond %{SCRIPT_FILENAME} /wp-content/uploads/Private/.+\.xls [NC]
    RewriteCond %{REQUEST_FILENAME} /wp-content/uploads/Private/.+\.xls [NC]

    As that says, this condition AND this condition must be met. You only want to try one or the other condition for this operation and for what you are trying to achieve. I just wasn’t sure which condition might work better.

    So your root .htaccess file would like like so:

    # BEGIN WordPress
    RewriteEngine On
    RewriteBase /
    RewriteRule ^index\.php$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]
    # END WordPress
    
    # BEGIN file lock-downs
    RewriteCond %{SCRIPT_FILENAME} /wp-content/uploads/Private/.+\.xls [NC]  
    RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_  
    RewriteRule ^ - [F]
    # END file lock-downs

    And I placed that in my site for my root .htaccess file and it worked as well. Please note, when trying to access the file directly in your browser, it makes a difference if the ‘p’ in /Private/ is lower case or upper case, so please make sure it’s how you have it set up when placing the URL in a browser. (I always just make sure that all letters in my directory names are always lower case so that I don’t have to worry about that.)

    • This reply was modified 6 years, 8 months ago by Andrew Nevins.
    Thread Starter RouseA

    (@rousea)

    I really appreciate your help.

    I have found that either of your suggestions (root .htaccess and Private/.htaccess) block documents from direct access. However, they also prevent the documents from being accessed from hyperlinks within the website (403 Forbidden).

    This suggests that REQUEST_FILENAME and SCRIPT_FILENAME are failing to recognise the source of the enquiry. Incidentally, I haven’t used both at the same time – I used it as a means of quickly switching to test each by commenting out the other.

    Is it possible to enable a log which may help identify any errors?

    I have configured within my Private directory a number of sub-directories (e.g. accounts, minutes etc). For test purposes I have used a .XLS file in the root Private directory, but it would be useful if the .htaccess file could cover all sub-directories to save having to duplicate it in each sub-directory.

    Also it would be good if the .htaccess file could cover all types of file that may be private (e.g. .XLSX, .DOCX, .PDF etc)

    I can’t believe that I am the only person with this requirement. Anyone who uses a Members Only password to protect private information stands the risk of it being accessed directly. The problem was brought to my attention by a bank security system that scans the internet for personal information and warned a member that his details were accessible, sending him the link to access the document directly.

    • This reply was modified 6 years, 9 months ago by RouseA.
Viewing 6 replies - 1 through 6 (of 6 total)

The topic ‘Query about htaccess’ is closed to new replies.