views:

27

answers:

2

Hi! I'm playing with .htaccess and I was wondering if with just an .htaccess inside the root directory is possible to block all the request from a browser directed on existing files or directories.

Let's try this example:

RewriteEngine On

RewriteBase /~my_user/my_base/

RewriteRule ^list/$ list.php [L]
RewriteRule ^element_of_list/([a-zA-Z0-9\-]+)/$ element.php?elem_id=$1 [L]

Now, if I write http://127.0.0.1/~my_user/my_base/list/, this is wroking fine but if I write http://127.0.0.1/~my_user/my_base/list.php it's still working. I don't want that. I want the user to obtain a 404 error in the last case.

We have /etc/apache2/mods-enabled/userdir.conf

<IfModule mod_userdir.c>
        UserDir public_html
        UserDir disabled root

        <Directory /home/*/public_html>
                AllowOverride All
                Options Indexes FollowSymLinks
                <Limit GET POST OPTIONS>
                        Order allow,deny
                        Allow from all
                </Limit>
                <LimitExcept GET POST OPTIONS>
                        Order deny,allow
                        Deny from all
                </LimitExcept>
        </Directory>
</IfModule>

My first try was to use RewriteCond:

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.*)$ 404.php [L]

But it's not working. Every request ends up redirected to 404.php

UPDATE

So I've managed to create the filter for directories:

RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_URI} !^/~my_user/my_base/$ [NC]
RewriteRule ^(.*)$ 404.php [L]

What it does is to check if the requested path (REQUEST_FILENAME) exists and it's a directory AND if it's not my RewriteBase which is basically index.php, then redirect to 404.php

I'm still trying to find something that does the same thing for files. I know I can selectively do that using extensions filename but I want an universal filter for files.

A: 

Try this rule:

RewriteCond %{THE_REQUEST} ^[A-Z]+\ /[^?\ ]*\.php[/?\ ]
RewriteRule .*\.php$ 404.php [L]

This will rewrite all requests whose paths contain a .php internally to 404.php.

Gumbo
That's ok, but what about directory? Is it possible to do it without using a php file to check them? The -d flag is quite weird. I think I'm not getting it.
dierre
Actually, why do you need the RewriteCond? What about RewriteRule ^(.*)\.php$ 404.php [L]
dierre
+1  A: 

If I've understood your requirements correctly, you're looking to do something like this:

# This is a real directory...
RewriteCond %{REQUEST_FILENAME} -f [OR]
# Or it's a real file...
RewriteCond %{REQUEST_FILENAME} -d
# And it's not 404.php...
RewriteCond $0 !=404.php
# And it's not the root
RewriteCond $0 !=""
# And it's not any of the above due to an internal redirect...
RewriteCond %{ENV:REDIRECT_STATUS} ^$
# So cause a 404 response (you could redirect to 404.php if you want)
RewriteRule ^.*$ - [R=404,L]

# Set the 404 error document
ErrorDocument 404 /~my_user/my_base/404.php

Keep in mind that this blocks everything that exists, so any images, stylesheets, or scripts will be sent to the 404 page too. If you just want to block access to the PHP files, Gumbo's solution is more appropriate. I think in that case you'll need another RewriteCond though to prevent looping:

# Make sure the reason this request has a .php is because it was requested
# by the user (and not due to a redirect)
RewriteCond %{THE_REQUEST} ^[A-Z]+\s/[^\s]+\.php
# Make sure we aren't on 404.php already
RewriteRule %{REQUEST_URI} !404\.php$
# We aren't, so redirect to 404.php
RewriteRule ^ 404.php [L]
Tim Stone
Yeah I want to block everything but just out of curiosity. Could you explain me the use of `$0` in there? Does it contain `${REQUEST_FILENAME}`? And you chained 5 `RewriteCond`, how does apache understand you need to confront the last three with the result of the first two? Does it just evaluate the first two before everything (in order of appearance)?
dierre
@dierre: The `$0` is a backreference to the `RewriteRule` test pattern (it contains the entire match from `^.*$`). It would have been possible to condense those two `RewriteCond` into the test pattern of the `RewriteRule`, I suppose, like `!^(|404\.php)$`. As for the chaining, there is an implicit `AND` that joins all `RewriteCond` together, unless you specifiy the `[OR]` flag.
Tim Stone
Relatedly, mod_rewrite processes the test pattern of the `RewriteRule` first, then tests each `RewriteCond` that is attached to the current rule (directly above it), starting at the top and working downwards. If all of that passes, then it performs the substitution in the `RewriteRule`.
Tim Stone
I was thinking, you use in `ErrorDocument` the full path of `RewriteBase`, is there any env:variable containing the value of `RewriteBase`?
dierre
@dierre: Not that I'm aware of. There's also not any way to have `ErrorDocument` use the file relative to the current directory, as far as I know. In that case, it might be better to just redirect to 404.php directly, like the `RewriteRule` in the second example, since then you wouldn't need to write the whole path again. Just be sure that 404.php returns a 404 status header in that case.
Tim Stone