Some Apache RewriteRules for Improved Security

A set of Apache RewriteRules, including curl commands to test them. Always test the rules, using a browser if possible, and curl. The curl output has been edited to make it easier to read.

Remember to write the rules carefully so you don’t deny access for valid requests, and use an appropriate 403 page, so real (good) people that arrive there have an opportunity to understand what happened and what they can do to request access. This may mean the rules blocked a valid request.

Route admin access through HTTPS

With the prevalence of laptops and WiFi, HTTPS is important for site security. This rule assumes the site is administered through a subdomain (admin.domain.com), and routes any request where the server name begins with admin through HTTPS.

Code

RewriteCond %{SERVER_NAME} ^admin
RewriteCond %{HTTPS} =off [NC]
RewriteRule .* https://admin.domain.com [L]

Test

[user@localhost Backup]$ curl -i http://admin.domain.com
HTTP/1.1 302 Found
Location: https://admin.domain.com

Block Probing Requests, XSS Injection, and Unwelcome Referrers

Even if your server doesn’t have these scripts or URLs, it is good to block the requests. People or servers that are requesting them are not visiting your site, they’re attacking it.

This includes referrers that simply don’t make sense, or are seen in the logs or stats requesting content they shouldn’t.

Code

RewriteCond %{REQUEST_URI} (\.aspx?|\.php)$ [NC,OR]
RewriteCond %{REQUEST_URI} (ldap|php\-?myadmin|scripts|mysql|wp\-login) [NC,OR]
RewriteCond %{QUERY_STRING} mouseover [NC,OR]
RewriteCond %{HTTP_USER_AGENT} (sleuth|morfeus|wget|python|curl|perl|scanner|apache\-httpclient) [NC,OR]
RewriteCond %{HTTP_REFERER} \.(ws|in|ru|ua|tv)/?$ [NC,OR]
RewriteCond %{HTTP_ACCEPT_LANGUAGE} en-us [NC,OR]
RewriteRule ^.*  - [F]

Tests

RewriteCond %{REQUEST_URI} (\.aspx?|\.php)$ [NC,OR]

This site is a PHP site which routes requests through RewriteRules, there should not be any direct requests for .php. Since it’s a PHP application, requests for .asp and .aspx should never be received either.

[user@localhost ~]$ curl -I http://domain.com/login.aspx
HTTP/1.1 403 Forbidden

RewriteCond %{HTTP_REFERER} \.(ru|ua|tv)$ [NC]

After too many requests from referrers ending in .ru, .ua, and .tv, I decided to block them.

[user@localhost ~]$ curl -I -e ‘http://some.ru’ http://domain.com
HTTP/1.1 403 Forbidden

RewriteCond %{QUERY_STRING} mouseover [NC,OR]

This rule is in response to some sort of XSS injection attack which included onmouseover.

[user@localhost ~]$ curl -I http://domain.com/?onmouseover
HTTP/1.1 403 Forbidden

RewriteCond %{REQUEST_URI} php\-?myadmin|scripts|mysql|wp\-login [NC,OR]

These are all common requests for variations of phpmyadmin, utility scripts, access to mysql and the login for WordPress. They should not be received by this server.

[user@localhost ~]$ curl -I http://domain.com/scripts
HTTP/1.1 403 Forbidden

RewriteCond %{HTTP_ACCEPT_LANGUAGE} en-us [NC,OR]

The site I’m protecting is delivered in US English. If US English isn’t one of the languages accepted by the client, the request will be denied.

Checking the latest visitors log verified the rules are working properly.

Host: 113.53.253.77

/index.php HTTP Response: 403 Date: Feb 24 07:46:56 Bytes: 629
/admin/index.php HTTP Response: 403 Date: Feb 24 07:46:57 Bytes: 633
/admin/pma/index.php HTTP Response: 403 Date: Feb 24 07:46:58 Bytes: 636
/admin/phpmyadmin/index.php HTTP Response: 403 Date: Feb 24 07:46:59 Bytes: 639
/db/index.php HTTP Response: 403 Date: Feb 24 07:47:01 Bytes: 631
/dbadmin/index.php HTTP Response: 403 Date: Feb 24 07:47:02 Bytes: 634
/myadmin/index.php HTTP Response: 403 Date: Feb 24 07:47:03 Bytes: 634
/mysql/index.php HTTP Response: 403 Date: Feb 24 07:47:04 Bytes: 634
/mysqladmin/index.php HTTP Response: 403 Date: Feb 24 07:47:05 Bytes: 637
/typo3/phpmyadmin/index.php HTTP Response: 403 Date: Feb 24 07:47:06 Bytes: 640
/phpadmin/index.php HTTP Response: 403 Date: Feb 24 07:47:07 Bytes: 635
/phpmyadmin1/index.php HTTP Response: 403 Date: Feb 24 07:47:10 Bytes: 637
/phpmyadmin2/index.php HTTP Response: 403 Date: Feb 24 07:47:11 Bytes: 637
/pma/index.php HTTP Response: 403 Date: Feb 24 07:47:12 Bytes: 632
/web/phpMyAdmin/index.php HTTP Response: 403 Date: Feb 24 07:47:13 Bytes: 640
/xampp/phpmyadmin/index.php HTTP Response: 403 Date: Feb 24 07:47:14 Bytes: 641
/web/index.php HTTP Response: 403 Date: Feb 24 07:47:15 Bytes: 632
/websql/index.php HTTP Response: 403 Date: Feb 24 07:47:17 Bytes: 634
/phpmyadmin/index.php HTTP Response: 403 Date: Feb 24 07:47:18 Bytes: 636
/phpMyAdmin/index.php HTTP Response: 403 Date: Feb 24 07:47:20 Bytes: 637
/phpMyAdmin-2/index.php HTTP Response: 403 Date: Feb 24 07:47:21 Bytes: 639
/php-my-admin/index.php HTTP Response: 403 Date: Feb 24 07:47:22 Bytes: 638
/phpMyAdmin-2.2.3/index.php HTTP Response: 403 Date: Feb 24 07:47:23 Bytes: 642
/phpMyAdmin-2.2.6/index.php HTTP Response: 403 Date: Feb 24 07:47:24 Bytes: 643
/phpMyAdmin-2.5.1/index.php HTTP Response: 403 Date: Feb 24 07:47:24 Bytes: 642
/phpMyAdmin-2.5.4/index.php HTTP Response: 403 Date: Feb 24 07:47:25 Bytes: 643
/phpMyAdmin-2.5.5-rc1/index.php HTTP Response: 403 Date: Feb 24 07:47:31 Bytes: 646
/phpMyAdmin-2.5.5-rc2/index.php HTTP Response: 403 Date: Feb 24 07:47:32 Bytes: 646
/phpMyAdmin-2.5.5/index.php HTTP Response: 403 Date: Feb 24 07:47:32 Bytes: 643
/phpMyAdmin-2.5.5-pl1/index.php HTTP Response: 403 Date: Feb 24 07:47:33 Bytes: 646
/phpMyAdmin-2.5.6-rc1/index.php HTTP Response: 403 Date: Feb 24 07:47:34 Bytes: 646
/phpMyAdmin-2.5.6-rc2/index.php HTTP Response: 403 Date: Feb 24 07:47:35 Bytes: 646
/phpMyAdmin-2.5.6/index.php HTTP Response: 403 Date: Feb 24 07:47:36 Bytes: 643
/phpMyAdmin-2.5.7/index.php HTTP Response: 403 Date: Feb 24 07:47:37 Bytes: 643
/phpMyAdmin-2.5.7-pl1/index.php HTTP Response: 403 Date: Feb 24 07:47:38 Bytes: 646

* One last note. If you block curl, testing with curl must set the user agent to something other than curl, or every test request will be blocked by the user agent rule.