Last week, we released the final version of our Dynamic IP Restrictions module for IIS 7.x. The feature is also built into IIS 8.0, which is included with Windows 8 and Windows Server 2012.
At the highest level, Dynamic IP Restrictions works by looking at incoming client IP addresses and allowing the server administrator to restrict either the maximum number of concurrent connections, or throttle the rate of incoming requests.
For information on how to configure and use Dynamic IP Restrictions, we have published a help topic here with screen shots that show all of the settings.
Requests behind a proxy
We’ve been showing this feature for some time now at various events, like Tech Ed, and one question invariably comes up: What about clients behind a proxy?
To handle that situation, Dynamic IP has a checkbox near the bottom of the page in Internet Services Manager entitled “Enable Proxy Mode”. But what does this setting actually do?
Proxy Mode allows Dynamic IP Restrictions to work with the client request header X-Forwarded-For to determine the IP address of the original client. Wikipedia has a pretty good description of the header here.
The way it works in general is that, when a proxy server gets a request from a client, it adds the client’s IP address to the end of a comma separated list of client IPs. Assuming that a request goes though multiple proxies, you would get an X-Forwarded-For header that looks something like this (where you’d see IP addresses in place of the names below):
X-Forwarded-For: original-client, first-proxy, second-proxy
Dynamic IP Restrictions can therefore determine the IP address of the original client.
But how can you trust data from the client?
Right after we explain support for the X-Forwarded-For header, the immediate next question is: How can you trust data from the client?
This is actually a pretty insightful question, and I really enjoy hearing questions that demonstrate the knowledge of our users. The ultimate answer to this question is that we can’t trust data from the client request without help from the server administrator. And this is where the behavior of Proxy Mode is not as intuitive as it could be. In order for it to be useful (or even functional), there is another configuration setting that needs to be made.
Specifically, we look at the X-Forwarded-For header only if Proxy Mode is enabled *and* we recognize the IP address of the client as a known safe proxy. A proxy server is considered safe only if its IP address is registered in the Allowed IP Address list. The logic works like this:
1) A new request arrives at the server. We get the IP address of the client from the network stack. This address is trusted, since it came from the local machine’s network software and not from the HTTP request.
2) If that IP address is on the Allowed list, we then know that it was the last entity to touch the X-Forwarded-For header and we consider the last entry on X-Forwarded-For to be a valid client IP address. If the network stack IP address is not on the Allowed list, then we consider the client IP address to be the value from the network stack and will use it. Otherwise, we go on to step 3.
3) We know that the last entry on X-Forwarded-For is valid, so we then check to see if it is on the Allowed list. If it is, then we know that the next-to-last entry on X-Forwarded-For was put there by a trusted proxy and we consider it valid. If the last entry is not on the Allowed list, then we consider it to be the IP address and use it.
We keep working our way from the end of X-Forwarded-For, towards the beginning until we reach an IP address that is not on the Allowed list. In this way, we know which entries to trust because of the proxy servers listed in the Allowed IP List.
Now let’s look at an example. Consider the following value for X-Forwarded-For:
Further, let’s assume that this request came through a load balancing proxy with the IP address 184.108.40.206, which is included on the Allowed list.
When the request arrives at our server, we can see that it came from the proxy with the IP address 220.127.116.11. Since this address is on the Allowed list, we look at the last entry in X-Forwarded-For and get 18.104.22.168. Since this address is not on the allowed list, we will use 22.214.171.124 as the IP address for applying dynamic IP restrictions.
Let’s look at another example. In this case, let’s assume that the client is trying to bypass dynamic IP restrictions by forging an X-Forwarded-For header. Finally, let’s say that Proxy Mode was enabled, but the request didn’t come through any proxy.
When the request arrives at our server, we’ll get the client’s actual IP address; let’s say it’s 126.96.36.199. Since 188.8.131.52 is not in the Allowed IP Addresses list, we don’t even look at the X-Forwarded-For header. We use the client’s actual IP address for dynamic IP restriction processing.
Finally, let’s look at a more complicated scenario, with a malicious client that is using your internet facing IP address in a fraudulent X-Forwarded-For header. This is a clever thing to do because it assumes that your internet facing server is a proxy. And let’s say that this assumption is correct. To complete the scenario, let’s say that they are going through 2 proxies, both of which are trusted by inclusion on then Allowed IP Address list and have the addresses 184.108.40.206 and 220.127.116.11. The X-Forwarded-For header now looks like this:
X-Forwarded-For: 18.104.22.168, 22.214.171.124, 126.96.36.199
When the request arrives at our server, we can see that it came directly from the proxy with the IP address 188.8.131.52. Since that IP address is on the Allowed list, we know that the last entry appended to X-Forwarded-For was put there by our trusted proxy.
So now we know that the last entry on X-Forwarded-For can be trusted. This entry is 184.108.40.206. We look up this value in the Allowed IP Address list and find it there.
We now know that the entry just before the previous one can be considered valid. This entry is 220.127.116.11. When we look it up on the Allowed IP Address list, we do not find it. We Therefore use 18.104.22.168 as the IP address for dynamic IP restrictions.
As you can see from these examples, we will never look at an entry in X-Forwarded-For unless we can validate its legitimacy by using the Allowed IP Address list.
How do I find the IP address of trusted proxies?
In our research on this topic, we’ve found that the majority of trusted proxies are going to be load balancers and reverse proxies that are owned by the same owner as the IIS server. In these cases, the IP addresses will be well known.
Finding the IP addresses of forward proxies out on the internet is a bit tougher question. I’ve spoken with a number of people concerned that many of their web site visitors are coming from within corporations or other places where a large number of users are behind such a proxy. This is. admittedly, a harder problem to solve.
My suggestion to address this would be to use the IIS logs to find cases where Dynamic IP Restriction has been triggered. This is easy to do by searching to logs for specific substatus codes. In particular, Dynamic IP Restrictions uses the following substatus codes:
501: Dynamic IP Restrictions rejected the request due to too many concurrent requests
502: Dynamic IP Restrictions rejected the request due to too many requests over time
Of course, these substatus codes may appear in combination with other HTTP status codes. This is because Dynamic IP Restrictions allows you to customize the HTTP response in the case of rejection.
Another very handy feature of Dynamic IP Restrictions is that you can use Logging Only Mode. In Logging Only Mode, Dynamic IP Restrictions runs its normal logic but does not reject any requests. Instead of rejecting requests, it will return the normal response and include one of the above substatus codes in the log to indicated that what it would have done in normal mode. With this feature, you can test out settings without actually interrupting your traffic.
The good news on this topic is that the anecdotal evidence we are seeing from early adopters is that this is not a common issue. What we are hearing is that traffic volumes subject to this issue are generally not high enough to trigger errant rejections. Of course, your specific situation may vary and you will need to tune your settings to find the proper balance.
The key takeaway here is that checking the “Enable Proxy Mode” box is only part of the process of supporting proxy server with Dynamic IP Restrictions. You also need to specify the IP addresses of proxies that you wish to trust. Also, you can be confident that when properly configured, Dynamic IP Restrictions works well with the X-Forwarded-For header and that clients cannot escape your restrictions by forging a bogus header.