Meaning of apache2 CONTEXT_DOCUMENT_ROOT and CONTEXT_PREFIX? - apache

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>

Related

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

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.

%{DOCUMENT_ROOT} variable inside If statement in Apache 2.4

in Apache 2.4.6 (the up-to-date version from CentOS 7 repository) I am trying to pass all the PHP files for processing to a specially compiled PHP-FPM in case that "php7.enable" file is present in the root of the website.
If I try this:
<If "-f %{DOCUMENT_ROOT} . '/php7.enable'">
RewriteEngine On
RewriteRule "^/(.*\.php(/.*)?)$" "fcgi://127.0.0.1:9000/%{DOCUMENT_ROOT}/$1" [P]
</If>
the condition works, but the %{DOCUMENT_ROOT} on the RewriteRule line is not expanded to the variable value, but is everytime empty. Strange is, that outside the "If" block, the expansion works.
Does anybody have any idea why and how to make it work ?
Thanks.
I've found a workaround which is the solution of my problem:
<FilesMatch "\.php$">
Require all granted
<If "-f '%{DOCUMENT_ROOT}/php7.enable'">
SetHandler proxy:fcgi://127.0.0.1:9001
</If>
</FilesMatch>
so I don't need the DOCUMENT_ROOT value at all and it works perfectly.
If I put "php7.enable" file to the DOCUMENT_ROOT of the website, all PHP files are being interpreted by PHP-FPM (running PHP7). If there's no such file found in the DOCUMENT_ROOT, the Apache handler is used to let the PHP interpret by (OS built-in) PHP5.
Thanks for all the ideas, it helped me to find a way to the root cause of the problem (with VirtualDocumentRoot in the config file the DOCUMENT_ROOT is probably not filled-in correctly).
It can be also seen in this question - Mass virtual hosting with Apache 2.4

How do I write an .htaccess file so that it can proxy images outside of the web root on an Apache server?

I want to proxy a bunch of images on my Apache server so that they are not stored in the webroot.
Specifically, I have all my images in the following folder on my Linux server:
/var/www/img/
However, I want it so that when a user goes to mydomain.com/img/img1.jpg (which has the server path /var/www/html/img/img1.jpg), it references the following file outside of the webroot:
/var/www/img/img1.jpg
It seems like this is possible using the ProxyPass and ProxyPassReverse rules in an .htaccess file (source: https://httpd.apache.org/docs/2.4/rewrite/avoid.html#proxy), but I'm having trouble understanding their syntax and which path goes where, etc.
Given my above situation, could someone please provide some explicit code that I can write into an .htaccess file to achieve what I want?
Edit: I just solved this problem by adding the following one line to my Apache httpd.conf file, and then restarting the server:
Alias "/img" "/var/www/img"
Where the /img part refers to the img directory in my webroot, and the /var/www/img part refers to the Linux filesystem directory I want to point to with the actual files in it.
Best way is to add a symbolic link to your other folder:
ln -s /my/target/folder /var/www/html/mynewfolder
If you can edit the Apache conf file for the server you need to add the FollowSymLinks directive in the directory block:
<Directory "/var/www/html/">
AllowOverride All
Options FollowSymLinks
</Directory>
You might also be able to add that to your .htaccess file as Options +FollowSymLinks if you can't edit the Apache file
You can try doing this with the PassThrough PT flag and mod_rewrite.
You create an alias to the actual path and then use it in the rule.
Alias "/img" "/var/www/img/"
RewriteRule "img/(.+)\.(jpe?g|gif|png)$" "/img/$1.$2" [PT]
See how that works for you.

Redirect images in .htaccess

I have two domains pointing to one application with different directories that is frontend on www.frontend.com and backend on www.backend.com. I have placed all images in the frontend/uploads folder, while on backend I cannot access the images on frontend/uploads.
How can I redirect all ^/uploads to www.frontend.com/uploads using htaccess?
I have tried this:
RewriteCond %{REQUEST_URI} ^/uploads/(.*)$
RewriteRule ^uploads/(.*)$ http://frontend.com/uploads/$1
Personally I'd just use a filesystem level symbolic link, as far more efficient (Unix, Linux, BSD, Darwin {OS X}) eg.
ln -s /srv/www/frontEnd/htdocs/uploads /srv/www/backEnd/htdocs/uploads
and make sure you have the FollowSymLinks option set in the backend Directory block eg.
<Directory /srv/www/backEnd/htdocs/uploads>
Options Indexes FollowSymLinks
</Directory>
But assuming the www.frontend.com site is accessible to your www.backend.com users all you need in your backend config is:
RewriteRule ^/?uploads/.* http://frontend.com%{REQUEST_URI} [NC,L,R=301]
If you take this approach I'd stick the rule in the httpd.conf, rather than a .htaccess, as that file is only parsed once on server startup, and the rule compiled, rather than having to parse the file for every request.

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]