Rewrite directory path based on authentication data - apache

I'm trying to provide my users a unique directory under one common URL (https://example.com/sync). Previously I managed this with a rewrite rule which just appended the remote users name to the root directory for "sync". Now, the users login ID differs from the directory name. As per apache documentation, authn_dbd provides additionally returned columns in extra variables with the prefix AUTHENTICATE_.
<Directory "/srv/www/sync/">
AuthDBDUserPWQuery "SELECT passphrase, identifier FROM webserver.fn_authenticate_context('SYNC') where login_id = %s"
RewriteEngine On
RewriteCond %{AUTHENTICATE_IDENTIFIER} ^(.+)$
RewriteRule ^\/(.*)$ /%{AUTHENTICATE_IDENTIFIER}/$1 [NS,L]
</Directory>
This should provide the required identifier for my rewrite rule. However, the identifier seems not to be available when rewriting occurs. Adding a header with the content to the response works and provides the content.
Activating logs up to trace8 shows that authentication is processed first and afterwards the rewrite conditions are processed but the value is still empty.

After searching around for quite a while, I found no reliable way to use the different "features" of Apache dbd. It produces variables for additionally returned columns - or not. The variables are available in CGI but not before or even not there. Errors are only logged during startup phase and later silently discarded. So if you don't get successful authentication at all, the root cause may be a permission problem. The only way to verify this is by executing the request with the credentials that dbd uses for access to the database.
The solution to my original issue is to forget about the documented option of additional columns from the authentication request and running an additional, separate request to the database. The working snippet:
### Cloud Synchronization (Web DAV service)
Define _SYNC_CLOUD_PREFIX /sync
# We need the rewrite engine here...
RewriteEngine On
# define a rewrite map which looks up the home directory for the user...
RewriteMap dbd_sync_home "dbd:SELECT dir_home FROM webserver.authorize_for_context('SYNC', %s);"
# redirect /sync to /sync/
RedirectMatch permanent ^${_SYNC_CLOUD_PREFIX}$ ${_SYNC_CLOUD_PREFIX}/
# Rewrite /sync/xxx to /srv/www/sync/<domain>/<user>/xxx
RewriteCond %{LA-U:REMOTE_USER} ^(.+)$
RewriteCond %{REQUEST_URI} ^${_SYNC_CLOUD_PREFIX}
RewriteRule ^${_SYNC_CLOUD_PREFIX}/(.*)$ /srv/www/sync/${dbd_sync_home:%{LA-U:REMOTE_USER}}/$1 [L]
# Authorize when hitting the location /sync/
<Location "${_SYNC_CLOUD_PREFIX}/">
DAV On
SSLRequireSSL
Options +FollowSymLinks
AuthType Basic
AuthName "Sync Heaven"
# To cache credentials, put socache ahead of dbd here
AuthBasicProvider socache dbd
AuthnCacheContext www-sync
AuthnCacheProvideFor dbd
# mod_authn_dbd SQL query to authenticate a user
AuthDBDUserPWQuery "SELECT passphrase FROM webserver.authorize_for_context('SYNC', %s);"
Require method OPTIONS
Require valid-user
</Location>
UnDefine _SYNC_CLOUD_PREFIX

Related

Block access to PHP file using .htaccess

I want to block direct access to PHP file, for example when someone enters it manually in the address bar (https://example.com/php/submit.php). I used this code:
<Files "submit.php">
Order Allow,Deny
Deny from all
</Files>
But if I block it this way, the form can't be submitted (it doesn't send mails).
Is there another way to block direct access but to be able to submit form?
<form id="form" action="php/submit.php" method="post">
Your form is making a POST request, whereas "when someone enters it manually in the address bar" they are making a GET request. So, you could block anything but POST requests..
Using <LimitExcept>
For example, surround your existing directives in a <LimitExcept> container:
<LimitExcept POST>
<Files "submit.php">
Require all denied
</Files>
</LimitExcept>
Note that this blocks non-POST requests to any submit.php file on your system.
NB: Order, Allow and Deny are Apache 2.2 directives and formerly deprecated on Apache 2.4 (which you are more likely to be using, unless you are on LiteSpeed). Require all denied is the Apache 2.4 equivalent. However, you should not mix authentication directives from both modules.
Using mod_rewrite
Alternatively, using mod_rewrite near the top of the root .htaccess file you can target the /php/submit.php URL-path directly. For example:
RewriteEngine On
RewriteCond %{REQUEST_METHOD} !=POST [NC]
RewriteRule ^php/submit\.php$ - [F]
The above will serve a 403 Forbidden for any request to /php/submit.php that is not a POST request (eg. when a user types the URL directly in the browser's address bar).
Alternatively, check that the request is a GET request. ie. =GET
HOWEVER, you should already be performing this check as part of your standard form validation in your PHP code, so this additional check in .htaccess should be redundant. (After all, how are you checking that the form has been (successfully) submitted?)

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.

.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.

mod_rewrite rule broken if PHP file is named as the rewrite pattern

I want to redirect all requests like:
/xml/doSomething?arg=value
To:
/xml.php?action=doSomething&arg=value
I tried this simple rule:
RewriteEngine
RewriteBase /
RewriteRule ^xml/([^/]+)[/]? xml.php?action=$1 [R=302,L,QSA]
But it not works. The xml.php is executed with arg=value param only. The problem is that there is that file named xml.php, as the first part of my rule. In fact, if I change the rule to:
RewriteRule ^asd/([^/]+)[/]? xml.php?action=$1 [R=302,L,QSA]
And I point to:
/asd/doSomething?arg=value
I'm correctly redirected to:
/xml.php?action=doSomething&arg=value
How the presence of a PHP file named as the first part of my rewrite pattern can break it all?
I solved. The problem is that I had Options MultiViews in my virtual host configuration. I did not know about this option:
A MultiViews search is enabled by the MultiViews Options. If the server receives a request for /some/dir/foo and /some/dir/foo does not exist, then the server reads the directory looking for all files named foo.*
That simply explain why my rule does not work.

How to prevent Apache / mod_rewrite from treating path as file with same name

I'm using WAMP Server, mostly configured as-is out of the box. I'm having trouble getting mod_rewrite to behave as expected locally (everything works fine on a production server).
I have a PHP file located at:
/ajax/graphs/get-graph.php
The way this file is normally invoked is via a bootstrap file loaded by
/index.php
I have a .htaccess file at the root with the following rules:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php [L]
So, basically, when my app requests via AJAX a call to /ajax/graphs/get-graph/it should be directed to /index.php.
The problem is, Apache/mod_rewrite sees the request path and loads /ajax/graphs/get-graph.php directly.
How do I prevent Apache from assuming that /ajax/graphs/get-graph/ is a valid file because a php file of the same name exists at that location?
It sounds like you've fallen into the trap of content negotiation ;-) As explained in the Apache documentation, there is an option called MultiViews which, when enabled, causes Apache to basically convert nonexistent directory names into their corresponding filenames.
The effect of MultiViews is as follows: if the server receives a request for /some/dir/foo, if /some/dir has MultiViews enabled, and /some/dir/foo does not exist, then the server reads the directory looking for files named foo.*, and effectively fakes up a type map which names all those files...
The intent is that you can have several versions of a file in different formats or languages, like
/some/dir
- foo.en.gif
- foo.en.png
- foo.en.jpg
- foo.fr.gif
- foo.fr.png
- foo.fr.jpg
and Apache will choose the best one based on the preferences provided by the browser.
To fix it, all you should need to do is add the directive
Options -MultiViews
in a <Directory> or <Location> block corresponding to /ajax/graphs. Or, if you don't have access to the main server configuration, you can put it in /ajax/graphs/.htaccess.