Welcome to part 3 of our series on securing a web directory in Apache using the .htaccess file.
In part one we looked at what the .htaccess file is and how it works. In part two we looked at the auth directive and how it can be used to provide authentication level access restrictions to a website directory. In case you haven’t yet, I’d recommend reading over the previous parts before continuing with this one.
In this article we will be looking at using the .htaccess file to either block or allow access based on where the request comes from.
Sometimes you may want users to be able to access part of your website based on where they are coming from, or alternatively deny users access from those locations. This is entirely stand-alone to the basic authentication method we discussed in part two, but you can use it in conjunction with that basic authentication in order to further restrict access and prevent attackers who may try to brute force the authentication method from even accessing it. Let’s look at a basic example below that could be added to a .htaccess file:
Allow from 192.168.1.2
Allow from example.com
Deny from all
First let’s look at the order directive. For those familiar with configuring firewalls, the way this works may seem somewhat strange as it effectively inverts the method that most firewalls use. Apache will first process all the directives that match the first directive type specified after the order directive, and then process all the directives that match the second type. The last matching directive is then applied. This is something that is probably best described with an example:
For that example let’s look at a connection from the IP 192.168.1.2, with the order set to “deny,allow” it will first be matched against the deny directives. It matches the “deny from all” directive so is set to be denied. Next, the allow directives will be evaluated and matched against “allow from 192.168.1.2”, so will then be set to allowed. With the evaluations complete the final state would be considered and access will be granted. Note here that this is opposite to how most firewalls work, where the first matching allow or deny would be applied and the order of the statements is important. With Apache, because the last read match is taken the order of the directives themselves becomes unimportant because the order directive controls the evaluation. Were the order set to “allow,deny”, as the “deny from all” directive would be read last then all incoming connections would be denied access regardless of where they came from. For those familiar with traditional firewalls this can make debugging the allow and deny directives confusing.
Now we’ll look in more detail as to the allow and deny directives themselves and the arguments they can take. Firstly, there is the all argument, which as it states will match all incoming traffic. Next you can use an IP address, part of an IP address or an IP address/CIDR mask pair as below:
Allow from 192.168.1.2
Deny from 10.1.0.0/20
Allow from 2001:db8::0/96
As you can see from the examples above, IPv6 is supported along with IPv4 with these directives if your server is using IPv6. When using a partial IP address Apache will match it to the beginning of the incoming address, so our above partial example will match any IP address starting with “172.16.0”.
The other method of matching for an allow or deny directive is with a hostname. When a hostname is used Apache will perform a reverse DNS lookup on the connecting IP address, and then perform a forward DNS lookup on that IP address, (only if both forward and reverse DNS match up with it) then evaluate the hostname of the IP address against the allow and deny directives. This match will be valid for any hostname that ends with the given one in the allow or deny directive. So in our original example, giving example.com an IP matching example.com will be allowed, but IPs matching badman.example.com and who.was.this.example.com would also be allowed.
Using a hostname can be very helpful if you use an ISP that provides you with a dynamic IP address, or an IP address that doesn’t stay the same forever, provided your ISP sets its forward and reverse DNS accordingly. For an example here, I’m using BT broadband here in the UK, where I’m writing this column and the RDNS for my current IP ends in btcentralplus.com, the hostname provided by the reverse DNS also resolves to my current IP address. So I could use “allow from btcentralplus.com” as a method of allowing myself access. Whilst this will also allow any other customers of my ISP (as their RDNS hostnames will also end with btcentralplus.com), it does enable me to block the majority of the internet from accessing the web directory I’m protecting.
By now you should have a fair idea on how to tackle the challenge of restricting access to particular directories of your website, but what if you only want to restrict access to specific files? Stay tuned for part four where I’ll be looking at that exact challenge.