Block access to PHP file using .htaccess - apache

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?)

Related

Apache - deny non-local requests that match a specific URL pattern

I need to configure my Apache (2.4) in a way, that it prevents answering specific requests
I have an application that runs on a server myapplication.com.
Browsing the application triggers some further ajax requests on the server, having the pattern myapplication.com/abc. These kind of "abc"-requests must be prohibited, when being called from a different context then this one. Thus, is should not be possible to call this request in a different tab. Neither should it be possible to call these requests outside a browser context, for example via curl or wget or any non-browser http client.
The directive must look something like this
<If "Request has not been triggered as ajax from within my application" && %{REQUEST_URL} contains 'abc' ">
Require all denied
</If>
This directive causes an error and I dont know why, changing =~ to !~ is semantically false but does not cause an error.
<If "%{HTTP_REFERER} !~ /myapplication.com/ && %{QUERY_STRING} =~ /abc/ ">
Require all denied
</If>
What can be wrong with that?
Any help debugging the apache config would also be appreciated - I don't get any information why this directive causes errors.
Any XMLHttpRequest or fetch call made from myapplication.com in browser will contain myapplication.com as referer header.
The following apache conf snippet will block requests which referrer isn't myapplication.com.
<Location "/abc">
RewriteEngine on
RewriteCond "%{HTTP_REFERER}" "!myapplication.com"
RewriteRule . - [R=403,L]
</Location>
This configuration requires mod_rewrite which can be enabled with a2enmod rewrite if not already done.

Rewrite URL in XAMPP VirtualHost

Been trying to play with mod_rewrite through .htaccess in one Xampp virtualhost, but I am not getting the results that I am looking for.
What I am tring to do is to rewrite the following: www.example.com/name/billy.html to: www.example.com/billy
Without trying to rewrite the URLs the virtualhost is working fine, I have access to all pages. However, when I add the .htaccess with the corresponding rewrite rule I get a 404 page not found. The regex is working as expected though. I see that the request to www.example.com/name/billy.html it's been rewritten to www.example.com/billy, but the page doesn't load.
The name folder exists in the file structure and the .htaccess is inside the example folder.
Currently, my vh configuration looks like this:
<VirtualHost *:80>
DocumentRoot "/Applications/XAMPP/xamppfiles/htdocs/example"
ServerName www.example.com
<Directory "/Applications/XAMPP/xamppfiles/htdocs/example">
Options Indexes FollowSymLinks ExecCGI Includes
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
And this is the content of the .htaccess file:
RewriteEngine On
RewriteRule ^name\/([a-z]+).html$ /$1 [L,NC,R]
What is missing?
What I am tring to do is to rewrite the following: www.example.com/name/billy.html to: www.example.com/billy
You seem to have the process the wrong way round and possibly mixing up "rewrites" and "redirects"?
You should be internally rewriting from the visible "friendly" URL to the underlying file-path that actually handles the request. You are trying to do the opposite here. How is your system expected to handle a request for /billy? (It doesn't, and generates a 404.)
You may be thinking you can change the URL using .htaccess (mod_rewrite) alone? But no, that is not how this works.
RewriteRule ^name\/([a-z]+).html$ /$1 [L,NC,R]
You mention "rewrite", but this directive is in fact a "redirect" (as indicated by the R flag). Specifically, a 302 (temporary) redirect in this instance.
(You might actually want to implement a redirect like this, if you are changing an existing URL structure, but more on that later*1)
A URL "rewrite" is entirely internal to the server. The user only sees the public URL, they do not see the URL that it might be rewritten to. You (the user) can't "see" a rewrite.
A "redirect" on the other hand, usually refers to an external redirect, ie. a 3xx response sent back to the client with an instruction to make a new request to a different URL. The URL being redirected to is visible to the user. This is used when content has moved to a different URL.
So, following your example, you should be requesting/linking to (in your HTML source) the short/friendly URL /billy and internally rewriting the request to /name/billy.html that actually handles the request.
For example:
RewriteEngine On
# Rewrite from "billy" to "name/billy.html"
RewriteRule ^[a-z]+$ name/$0.html [NC,L]
The $0 backreference contains the entire URL-path that is matched by the RewriteRule pattern.
Only use the NC flag if you do need to match uppercase letters as well. But a request for /Billy won't serve /name/billy.html on a case-sensitive OS.
And that's really it, with regards to the URL-rewritting, you can stop reading here.
*1 Redirect from old to new
Regarding the external "redirect" mentioned above. You might choose to implement a redirect (in the opposite direction) if you are changing an existing URL structure and the old URLs have been indexed by search engines and/or linked (or bookmarked) to by external third parties - in order to preserve SEO and keep users happy.
For example, say your original URLs were of the form /name/billy.html and you later decided to change your URLs to /billy instead. You first change the URLs in the HTML source and implement the "rewrite" as mentioned above so the new URLs now work. You then might implement an external redirect from the old /name/billy.html URL to the new /billy URL.
For this, you would use a directive like you had initially, except you have to be careful of redirect-loops because you are already rewriting the request in the opposite directive. You only want to redirect "direct/initial" requests and not rewritten requests by the earlier rewrite (that is actually later in the file). An easy way to check for "direct" requests is to check against the REDIRECT_STATUS environment variable, which is empty on the initial request and set to 200 (as in 200 OK status) when the request is rewritten.
For example, the following "redirect" would go before the above "rewrite", immediately after the RewriteEngine directive:
# Redirect from "name/billy.html" to "/billy"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^name/([a-z]+)\.html$ /$1 [R=301,NC,L]
This should ultimately be a 301 (permanent) redirect since the URL has presumably changed permanently. However, always test with 302 (temporary) redirects to avoid potential caching issues.
Further reading:
Reference: mod_rewrite, URL rewriting and "pretty links" explained

Rewrite directory path based on authentication data

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

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]

How can I block mp3 crawlers from my website under Apache?

Is there some way to block access from a referrer using a .htaccess file or similar? My bandwidth is being eaten up by people referred from http://www.dizzler.com which is a flash based site that allows you to browse a library of crawled publicly available mp3s.
Edit: Dizzler was still getting in (probably wasn't indicating referrer in all cases) so instead I moved all my mp3s to a new folder, disabled directory browsing, and created a robots.txt file to (hopefully) keep it from being indexed again. Accepted answer changed to reflect futility of my previous attempt :P
That's like saying you want to stop spam-bots from harvesting emails on your publicly visible page - it's very tough to tell the difference between users and bots without forcing your viewers to log in to confirm their identity.
You could use robots.txt to disallow the spiders that actually follow those rules, but that's on their side, not your server's. There's a page that explains how to catch the ones that break the rules and explicitly ban them : Using Apache to stop bad robots [evolt.org]
If you want an easy way to stop dizzler in particular using the .htaccess, you should be able to pop it open and add:
<Directory /directoryName/subDirectory>
Order Allow,Deny
Allow from all
Deny from 66.232.150.219
</Directory>
From this site: (put this in your .htaccess file)
RewriteEngine on
RewriteCond %{HTTP_REFERER} ^http://((www\.)?dizzler\.com [NC]
RewriteRule .* - [F]
You could use something like
SetEnvIfNoCase Referer dizzler.com spammer=yes
Order allow,deny
allow from all
deny from env=spammer
Source: http://codex.wordpress.org/Combating_Comment_Spam/Denying_Access
It's not a very elegant solution, but you could block the site's crawler bot, then rename your mp3 files to break the links already on the site.