Apache .htaccess <FilesMatch> // Setting as forbidden subfolder files - apache

I'm going mad over Apache .htaccess
I'm trying to setting as protected my subfolders using relative address, but it seems impossible.
The path of Apache folder is structured like this:
/var/www/apachedir
now I want to protect
/var/www/apachedir/subfolder/*
What I tryied is putting in /var/www/apachedir/ an .htaccess file like this
<FilesMatch "subfolder\/.*">
Order Allow,Deny
Deny from all
</FilesMatch>
but it seems not woking good.
I don't want to use ModRewrite and I want to make this .htaccess reusable.
So, listen, if I put the site over an other server that has a direcory structure like /var/www/zzz it has to protect files in /var/www/zzz/subfolder/*.
Also the file .htaccess has to stay in the root folder /var/www/apachedir.
There's a way to do it?
Edit:
I don't want to use ModRewrite but also I don't want to use Redirectmatch.
I want to know if there's a way to set it up with FilesMatch without ModRewrite or Redirectmatch.

I don't want to use ModRewrite.
You can use RedirectMatch to block access to a known path:
Redirectmatch 403 ^/subfolder/

I want to know if there's a way to set it up with FilesMatch
No, because the FilesMatch (and the non-regex Files) directive(s) literally match against files only, not directories. eg. <Files "*.jpg"> matches all .jpg files in any subdirectory.
There are various methods to block access to that subdirectory...
Use a <Directory> section in the server config
If you have access to the server (virtual host) config then you can use the <Directory> (and <DirectoryMatch>) directive(s) to target specific directories. But this is not permitted in .htaccess. For example:
<Directory "/var/www/apachedir/subfolder">
Require all denied
</Directory>
Create an additional .htaccess file in that subdirectory
The equivalent userland .htaccess way of doing this is to create an additional .htaccess file in that subdirectory (ie. at /subfolder/.htaccess) with a single Require all denied directive. The .htaccess file itself is equivalent to the <Directory> directive in the server config.
Aside: Order, Deny and Allow are Apache 2.2 directives and formerly deprecated on Apache 2.4 (which you are far more likely to be using). You should be using the equivalent Require (mod_authz_core) directives instead, as used above.
Use Redirect 403 (mod_alias) - not a "redirect"
I don't want to use ModRewrite but also I don't want to use Redirectmatch
RedirectMatch (and Redirect) are part of mod_alias - this is a base module and compiled into Apache by default (unlike mod_rewrite), so using the prefix-matching Redirect directive (no need for the regex variant RedirectMatch) is a reasonable solution as #anubhava suggests in his answer, depending on the scenario and existing directives. For example:
Redirect 403 /subfolder/
Despite the use of the Redirect directive, this is not an external (HTTP) redirect. The 403 response is served via an internal subrequest.
Set an environment variable and check with mod_authz_....
Alternatively, you can set an environment variable when the /subfolder is requested (using SetEnvIf) and check for this using the Require directive. This allows you to keep the condition separate from the directives that actually permit access. For example (using Apache 2.4 mod_authz_core):
SetEnvIf Request_URI "^/subfolder/" BLOCK_ACCESS
<RequireAll>
Require all granted
Require not env BLOCK_ACCESS
</RequireAll>
NB: If you are doing any URL-rewriting with mod_rewrite then you might need to check for REDIRECT_BLOCK_ACCESS instead in the above Require directive.
<If> expression (Apache 2.4)
On Apache 2.4 you can also use an <If> expression to target that specific subfolder with a containing mod_authz_core directive. For example:
<If "%{REQUEST_URI} =~ m#^/subfolder/#">
Require all denied
</If>
Although, strictly speaking, these methods target the URL-path, not the file-path.

Related

How "Apache will serve any file mapped from an URL" Directory directive?

I have a question about the Apache's Directory directive, here is what they say in the docs (http://httpd.apache.org/docs/2.4/mod/core.html#directory):
Note that the default access for <Directory "/"> is to permit all access. This means that Apache httpd will serve any file mapped from an URL. It is recommended that you change this with a block such as
<Directory "/">
Require all denied
</Directory>
But how will Apache do what they say (serve any file mapped from an URL) if I have only DocumentRoot set up, e.g.:
DocumentRoot "/usr/local/apache/htdocs"
No Alias "/some/webspace/path" "/", UserDir or other URL mapping rules which map to the root / directory of the system?
In another section of the docs (Security Tips http://httpd.apache.org/docs/2.4/misc/security_tips.html#protectserverfiles), they make a half-full example of UserDir as I can guess:
One aspect of Apache which is occasionally misunderstood is the
feature of default access. That is, unless you take steps to change
it, if the server can find its way to a file through normal URL
mapping rules, it can serve it to clients.
For instance, consider the following example:
# cd /; ln -s / public_html
Accessing http://localhost/~root/
This would allow clients to walk through the entire filesystem. To work around this, add the following block to your server's configuration:
<Directory "/">
Require all denied
</Directory>
This will forbid default access to filesystem locations.
Is what they say about the Directory directive at http://httpd.apache.org/docs/2.4/mod/core.html#directory just a warning in the case you use modules like mod_userdir like they then show at http://httpd.apache.org/docs/2.4/misc/security_tips.html#protectserverfiles? Or is there something else, maybe a little detail about Directory not given in the doc?
Thanks for the attention!
In Apache, there are unlimited hypothetical plugins/directives that could change only how a URL is mapped to the filesystem. Obvious/mainstream ones are DocumentRoot, Alias, AliasMatch, RewriteRule, UserDir etc. But there's no telling what other ones might exist.
Apache simply separates the URI to filesystem mapping completely from whether the core is willing to actually serve from that filesystem location.
There are a few ways you can accidentally expose things, like with mod_rewrite, and the default configuration file protects you from this with the defaults on <Directory />. The manual is not good at always emphasizing the differences in the compiled-in defaults and the contents of the default conf. The latter can change when repackaged which is tricky.

Meaning of apache2 CONTEXT_DOCUMENT_ROOT and CONTEXT_PREFIX?

How are the Apache2 (2.4) CGI environment variables CONTEXT_DOCUMENT_ROOT and CONTEXT_PREFIX defined?
From experimentation, I've determined the following:
CONTEXT_DOCUMENT_ROOT appears to be the full local path to the original request when DirectoryIndex or ErrorDocument call a CGI script.
CONTEXT_PREFIX appears to be the original REQUEST_URI, sans any query part, when DirectoryIndex or ErrorDocument have called a CGI script. (In these cases, REQUEST_URI is set to the URI of the CGI script, rather than the original.)
However, I can't seem to find any official documentation from Apache on these variables. Does anyone here have a link to such documentation, or more authoritative knowledge to share?
CONTEXT_PREFIX and CONTEXT_DOCUMENT_ROOT tell you how apache used an Alias directive (or similar feature - like mod_userdir) to translate the URL path to the file system path. The file system path will end up pointing to the the file to be served or a cgi script to run.
So, if apache translates the URL:
http://host/_CONTEXT_PREFIX/path/file
to the file system path:
/_CONTEXT_DOCUMENT_ROOT/path/file
it implies there is an Alias (or ScriptAlias or similar mechanism such as mod_userdir) like the following:
Alias /_CONTEXT_PREFIX /_CONTEXT_DOCUMENT_ROOT
The Alias directive saves /_CONTENT_PREFIX in ${CONTEXT_PREFIX} and /_CONTEXT_DOCUMENT_ROOT is saved in ${CONTEXT_DOCUMENT_ROOT}.
Here is one example of using it. In a <Directory> context RewriteRule translates a path name relative to the directory to a absolute URL path, or an absolute file name (which must exist). So if you wanted to translate URL's that ended in .htm to .php, you would have to write it like this:
<Directory "/_CONTEXT_DOCMENT_ROOT">
<FilesMatch "*.htm">
RewriteEngine On
RewriteRule (.*)[.]html$ /_CONTEXT_DOCUMENT_ROOT/$1.php
</FilesMatch>
</Directory>
Now you can write it without repeating /_CONTEXT_DOCUMENT_ROOT, but perhaps more importantly the file does not have to exist because we are rewriting it to a URL path:
<Directory "/_CONTEXT_DOCMENT_ROOT">
<FilesMatch *.htm>
RewriteEngine On
RewriteRule (.*)[.].* ${CONTEXT_PREFIX}/$1.php
</FilesMatch>
</Directory>

.htaccess in Multiple Environments

I know similar questions have been asked before but I haven't found any really specific answers for my situation.
I have an ExpressionEngine site running on multiple environments (local, dev, production), and each of those environments needs different .htaccess rules:
All Environments
Remove index.php
Set a 404 file
Set 301 Redirects
Development
Password Protect with .htpasswd
Force HTTPS protocol
Prevent search engine indexing with X-Robots-Tag
Production
Force HTTPS protocol
Redirect non-www subdomains to www
Local
No unique rules.
I've seen a lot of examples of how you can set specific environments per-module. Like RewriteCond %{REQUEST_HOST} ^dev.myurl.com for the mod_rewrite module, and tricks like this for .htpasswd requirements.
But what I would really prefer is some way to set global environment variables, then re-use those variables in the .htaccess file per-environment. To use pseudo-javascript as an example, something like:
var local = 'mysite.local';
var development = 'dev.mysite.com';
var production = 'www.mysite.com';
// Global .htaccess rules
if(environment == local){
// Local environment .htaccess rules
}
if(environment == development){
// Development environment .htaccess rules
}
if(environment == production){
//Production envirotnment .htaccess rules
}
This way all of the environment-specific rules are all grouped together, making a really clean file, and only one variable needs to be changed if an environment is changed.
I've seen a few references to altering settings in Apache's config files, but obviously that's not a viable option if I'm dealing with 3rd-party hosts.
So is this pie-in-the-sky wishful thinking, or can it be done?
Jon's answer is a good one. Unfortunately, not all web hosts will allow you to control that -D parameter for starting Apache.
Here's a way to use a single htaccess file on dev and production, but only have the dev site password protected:
# ----------------------------------------------------------------------
# Password protect staging server
# Use one .htaccess file across multiple environments
# (e.g. local, dev, staging, production)
# but only password protect a specific environment.
# ----------------------------------------------------------------------
SetEnvIf Host staging.domain.com passreq
AuthType Basic
AuthName "Password Required"
AuthUserFile /full/path/to/.htpasswd
Require valid-user
Order allow,deny
Allow from all
Deny from env=passreq
Satisfy any
So is this pie-in-the-sky wishful thinking, or can it be done?
IMO, yes. You're never going to be able to get predictable "scoping" of rules based on ENV variables or anything like that. There doesn't exist arbitrary if(something) { do everything in here } in apache. Lots of directives won't work inside certain scopes, and in later on, when you need to change how something works, you're more likely to break what you have than simply amending it.
The best way is to not use htaccess files at all:
You should avoid using .htaccess files completely if you have access to httpd main server config file. Using .htaccess files slows down your Apache http server. Any directive that you can include in a .htaccess file is better set in a Directory block, as it will have the same effect with better performance.
Create a separate vhost for local, dev, and production. Turn them on or off as needed, whatever global config they share, store that elsewhere (like in a file called global.includes) and then use the Include directive in all 3 vhosts. If you need to apply rules to specific directories, use the <Directory> block instead of htaccess files.
If you'd rather stick everything inside htaccess files, you could try putting everything in <IfDefine> blocks, it's probably the closest thing you'll have to your pseudo-code in your question. Essentially something like:
# Global htaccess rules
RewriteEngine On
RewriteRule ^foo$ /bar [L]
# Only local
<IfDefine LocalInstance>
RewriteRule ^local/foo /bar [L]
</IfDefine>
# Only dev
<IfDefine DevInstance>
RewriteRule ^dev/foo /bar [L]
</IfDefine>
# Only production
<IfDefine ProductionInstance>
RewriteRule ^dev/foo /bar [L]
</IfDefine>
Then when you start apache, you'd need to pass in -DLocalInstance, -DDevInstance, or -DProductionInstance as command line paramaeters or using the Define directive (with only one argument) somewhere in your vhost config. This isn't guaranteed to work as smoothly as it looks like it should, I've ran into unexplained issues with <IfDefine> before, especially if you try to get too fancy.

Apache Mod_ReWrite Suddenly Stopped Working

I had mod_rewrite set on my server to rewrite a url like the following
http://www.example.com/1
to
http://www.example.com/index.php?show=1
In order words a URL shortern. Everything was working fine when the system was running under a sub-domain on my development site, but now it just generates a Not Found error, although if I manually enter the url /index.php?show=1 it works fine.
So the only changes is the urls switching from
http://www.site.example.com
to
http://www.site.com
however it's still running on the same server and the same sub-folder inside public_html on the server just the new domain name has been pointed to that folder.
The folder it's stored in is /public_html/paste
The full .htaccess file running in the directory is
# Set Default File
DirectoryIndex index.php
# Turn ReWrite Engine On
RewriteEngine on
# Create Rule To Write URLs To Shorter Versions
RewriteRule /([a-z0-9]+) /index.php?show=$1
I can't enable RewriteLog as the hosting doesn't allow it for some reason.
It sounds like the AllowOverride directive is not properly set for that folder. In your Apache configuration, you should make sure that the Directory or Vhost you're using for the primary domain has the AllowOverride set to All
http://httpd.apache.org/docs/2.0/mod/core.html#allowoverride
You probably need to specify the RewriteBase directive.
http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase
I'll also note that Options +FollowSymlinks would be good to have in there too in case you ever turn it off further up the config chain (rewrite wont work without it).

Restrict access to a directory by file type

My Google-fu is failing me on this one...
I'm trying to create an Apache config that will only allow access to image, js, and css files in a specific directory.
For example, the following URL should work:
mysite.com/dir/image.gif
but this should be blocked:
mysite.com/dir/page.php
The part I'm struggling with is getting it working only for /dir/. The rest of the directories outside of /dir/ shouldn't be impacted by this directive.
This is what I have so far, which isn't doing what I need (it seems to apply to all directories).
<FilesMatch "\.(gif|jpe?g|jpg|png|js|css)$">
Order deny,allow
Allow from all
</FilesMatch>
How do I only allow access to certain file types within /dir/ but not affect the rest of my directories?
I recently used this:
Options -ExecCGI -Indexes
<FilesMatch "\.*$">
deny from all
</FilesMatch>
<FilesMatch "\.(png|jpg|gif|css)$">
allow from all
</FilesMatch>
I could not find explicit documentation on this but for FilesMatch it appears Apache does not short-circuit at the first match. It processes the entire .htaccess rules.
So the first rule blocks access to all file types and the second then allows the selected types.
Probably needs more testing but had to do something for a client that was easy for them to implement to deal with a web exploit their developers are struggling to fix.
For simplicity, when I do this I usually put all the media files in their own directory. However if this isn't an option you might try the FilesMatch directive:
http://httpd.apache.org/docs/2.2/mod/core.html#filesmatch
You can put a FilesMatch inside a Directory.
I'd generally use mod_rewrite for that
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} ^/my_dir/[^/]+\.php$
RewriteRule .* - [F]