In the previous part of this article, we have reviewed the basic steps to better protect files from accidental disclosure and looked at how the basics of Apache configuration works. You can find the article here: Protect files against unwanted access with Apache.
There are a few more different ways to further extend protective measures through Apache, most of them are quite simple to configure.
Password protection
All web-based content management systems implement some sort of user authentication and password protection, however, it all happens inside the CMS and only applies to program files or the administrative area of the CMS itself.
It is possible to protect files on Apache level so unauthenticated requests are blocked before they’d even hit anything in the protected folder. This kind of password protection can easily be recognized a standalone browser authentication window pops up.
To achieve this, you need to first create a password file often called “.htpasswd” that contains username and encrypted password pairs. While there are numerous web-based password tools available but it’s generally a bad idea to generate passwords using someone else’s computer (or website). They are useful to test configuration though so I’m including a link to one of them (unaffiliated, use at your own risk) here: https://www.askapache.com/online-tools/htpasswd-generator/
To generate these password pairs, Apache includes a utility called “htpasswd” that is capable of making a .htaccess file in the proper format, so it will look like this:
username1:hash1
username2:hash2
...
“htpasswd” usage:
$ htpasswd --help
Usage:
htpasswd [-cimBdpsDv] [-C cost] passwordfile username
htpasswd -b[cmBdpsDv] [-C cost] passwordfile username password
htpasswd -n[imBdps] [-C cost] username
htpasswd -nb[mBdps] [-C cost] username password
-c Create a new file.
-n Don't update file; display results on stdout.
-b Use the password from the command line rather than prompting for it.
-i Read password from stdin without verification (for script usage).
-m Force MD5 encryption of the password (default).
-B Force bcrypt encryption of the password (very secure).
-C Set the computing time used for the bcrypt algorithm
(higher is more secure but slower, default: 5, valid: 4 to 31).
-d Force CRYPT encryption of the password (8 chars max, insecure).
-s Force SHA encryption of the password (insecure).
-p Do not encrypt the password (plaintext, insecure).
-D Delete the specified user.
-v Verify password for the specified user.
On other systems than Windows and NetWare the '-p' flag will probably not work.
The SHA algorithm does not use a salt and is less secure than the MD5 algorithm.
To create a new .htpasswd file and add a new user, run
$ htpasswd -c .htpasswd techtipbits
New password:
Re-type new password:
Adding password for user techtipbits
$ cat .htpasswd
techtipbits:$apr1$/YUFLz2Q$Q5DB1B/573lEbG3bU6fZZ1
As you can see, the file contains the username (techtipbits) and a password hash. Hashes are one-way encrypted representations of the password, making it more difficult to find out the original password by looking at the hash (in this case it was “123qweasd”)
To make Apache use this to protect a particular folder, put this block in the .htaccess file of the folder:
AuthType Basic
AuthName "Protected area"
AuthUserFile /full/path/to/.htpasswd
Require valid-user
The AuthUserFile directive should, unfortunately, contain the full path to the .htpasswd file, relative paths are not allowed. What this means is that you’ll need to find out what the full path of the folder of .password is, by either contacting your webhost, looking it up in the control panel or asking your friendly system administrator.
If your installation is capable of running PHP and you can’t figure out the absolute path, you can create a simple test script in the same folder, for example, “test.php” containing this one-liner:
<?php echo __DIR__; ?>
Opening this file from the browser as “https://yourwebsite/folder/test.php” will display the current folder name. Remember to remove this file after the testing is done.
If you get the authentication window but it just won’t accept your password, double-check if the .htpasswd file is at the right location and if the username/password matches. You can use the sample file above for a quick test, but be sure to change it afterwards.
Limiting access to specific countries
IP addresses rarely ever move or change owners so it’s possible to build a database that connects IPs to specific countries. It’s not 100% effective but provides a great way to reduce the possible attack surface, especially when using it on otherwise sensitive areas of a website, for example the WordPress administrative area. Read more about securing WordPress here: How to better protect and secure WordPress
There is a free GeoIP database that’s included with most Linux distributions, courtesy of the company Maxmind. They also provide free updates (requires registration, personal use only) to their IP to country database. It can be easily installed on Linux (Debian/Ubuntu) by running apt install geoip-database. By enabling the mod_geoip module in Apache (apt install libapache2-mod-geoip) it becomes possible to quickly identify and block requests depending on the country of the IP address.
It’s a good idea to only selectively enable for parts of the website that needs protection because it costs CPU resources to resolve the country for each request and its IP address.
By adding this block to your .htaccess file, it only allows requests from a specific country (in this case US):
GeoIPEnable On
SetEnvIf GEOIP_COUNTRY_CODE US AllowCountry
Deny from all
Allow from env=AllowCountry
If you get an 500 error after doing this, it means that GEOIP is not enabled in Apache, you need to either do it yourself (needs root, see instructions above) or contact your webhost or sysadmin.
It’s easy to test this by changing the country code in the SetEnvIf line to something else and verify that you get a 403 forbidden error.
Here is a bit more complicated example, this is to protect WordPress logins from one country only.
<IfModule mod_geoip.c>
<Files ~ "^(wp-login|xmlrpc).php$">
GeoIPEnable On
SetEnvIf GEOIP_COUNTRY_CODE US AllowCountry
Deny from all
Allow from env=AllowCountry
</Files>
</IfModule>
Notice how the whole block is wrapped in an <IfModule> statement it only runs if the GeoIP module is enabled. The advantage of this is that it gracefully ignores settings that would otherwise cause a 500 error due to lack of the module. The disadvantage of this is that if somehow mod_geoip is disabled on the server, it will silently open the protected part to the whole world so depending on your security preference, you may want to either keep it in there or remove it.
The inner <Files> block limits GeoIP blocking to only files that match “wp-login.php” or “xmlrpc.php” – the URLs that are typically attacked to brute-force usernames/passwords by bad robots. It won’t protect you from all of them but at least anyone outside the US will automatically get a 403 forbidden error.
In part 3, we’ll look at blocking TOR and using client-side SSL certificates, stay tuned.