Here’s the problem — you’re hosting a Windows server running IIS on AWS, and your web app is getting hammered with requests that look like this:
https://www.mywebsite.com/?q=%23Hcker%3Cbody%20oninput%3Djavascript%3Aalert(1)%3EHcker
These are clearly malicious. You want to block the IP easily and programatically. My website fields thousands of requests just like this daily, ever since we made it onto HackerOne’s list of apps using Google APIs that have more than 50,000 active users.
You have a few options, but I argue you only have one good option.
Various options to block an IP in AWS
- You might think to use AWS Security Groups, but you’ll quickly realize that Inbound Rules in AWS Security Groups can only be used to whitelist traffic, not block traffic.
- When you dig further into what AWS offers, you’ll come across the ability to block individual IPs, and even ranges of IPs, using a Network ACL (Network Access Control List). Specifically, you can set ALLOW and DENY rules. That’s actually a decent solution, but there are two issues: 1) If you want to do this programmatically, you’ll need to learn the AWS Network ACL API; and 2) You are limited to 20 total rules, including Inbound and Outbound. So if you have 1 Outbound rule, which is normal, and 1 default Inbound rule, you’re left with only 18 IPs or ranges that can be blocked.
- If you dig even further into what AWS offers, you’ll find that you can integrate your own third-party firewall into your EC2 environment, but this solution will be complex and expensive.
- If you’re using Windows Firewall, then you can block an IP address using Windows Firewall, and you can even programmatically block IPs using the Windows Firewall API. This is like a decent option, but one I’ve never tried before. The AWS documentation encourages users to use Security Groups instead of Windows Firewall, so ever since I set up Security Groups, I have disabled Windows Firewall on my production servers.
- After exhausting your native AWS options, you’ll start to look at what kind of blocking IIS can do natively. You’ll come across Dynamic IP restrictions, but if your web app is even close to as complex as mine, you’ll note a few things lacking.
Why IIS’s Dynamic IP Restrictions Kind of Sucks
- It’s great to be able to stop IPs that send too many requests at once, but if you have any kind of busy web app, this will lead to false positives. What about images? Let’s say you have a page with 200 images, each of which will be a unique request. The user might get blocked after requesting this page even if you have this set to a reasonable threshold like 100.
- Most malicious attacks involve a query string. It would be nice to apply these rules just to requests that have a query string, but no such option exists.
What is the easiest and the best solution?
My preferred method of blocking IPs is by using the IP Address and Domain Restrictions function of IIS. It requires you to install the IP and Domain Restrictions module from Server Manager, but once installed, blocking an IP is as easy as:
- Use the IIS user interface to navigate to the IP Address and Domain Restrictions section to block an IP address. This can be done both on the individual site level by navigating to a specific site, or on a server-wide level by navigating up a few levels to the entire server.
- Add an ipSecurity item to a website’s web.config file, which is the same as adding the IP to an individual site in IIS.
- Add an ipSecurity item to the server’s ApplicationHost.config file, which is the same as adding the IP to the whole server in IIS. This file is found under c:\windows\system32\inetsrv\config.
- Use the Microsoft.Web.Administration API to add new IPs easily to the block list, which essentially writes new IP addresses to ApplicationHost.config.
Using the IIS API to block IPs server-wide
To programmatically block IP addresses in IIS, use the Microsoft.Web.Administration API. However, don’t install the Nuget package, as programmer Lex Li explains. Instead, reference the DLL that’s already part of your Windows OS.
Here’s the C# code I use personally:
public void IISBlockSingleIP(string ipAddress) { IPAddress ip; bool IsIPValid = IPAddress.TryParse(ipAddress, out ip); //IT'S VERY IMPORTANT THAT YOU VALIDATE THE IP, BECAUSE THE API WILL ACCEPT ANY TEXT AND ADD IT TO ApplicationHost.CONFIG //AND THIS WILL CORRUPT ApplicationHost.CONFIG AND TAKE DOWN EVERY WEBSITE ON YOUR SERVER if (IsIPValid) { using (ServerManager serverManager = new ServerManager()) { Configuration config = serverManager.GetApplicationHostConfiguration(); ConfigurationSection ipSecuritySection = config.GetSection("system.webServer/security/ipSecurity"); ConfigurationElementCollection ipSecurity = ipSecuritySection.GetCollection(); var addElement = ipSecurity.CreateElement("add"); addElement.SetAttributeValue("ipAddress", ipAddress); addElement.SetAttributeValue("allowed", "false"); ipSecurity.Add(addElement); serverManager.CommitChanges(); } } }
As I’ve noted in the comments, it’s critically important to validate every IP before committing it to ApplicationHost.config, because the API will accept any junk text and add it as a DENY rule — and if a non-IP makes it in there, it will bring your whole server down.
Again, all this code does is add a line to the ipSecurity section of ApplicationHost.config. So technically, you could manipulate the file yourself, without the Microsoft API, but since this is just a few lines of code, it’s much easier than risking corrupting the file with malformed XML.
Why bother with blocking an IP programmatically when you can just as easily do it manually through the IIS interface?
I block IPs almost weekly because they’re pounding my server with malicious requests. I detect these requests in the Application_BeginRequest method of my Global.asax file, and then I send myself an email when an IP meets certain criteria. That email then contains a link which calls the above-referenced code. So blocking an IP for me is as easy as getting an email notification and clicking a link. It’s much easier than RDPing into my web server, launching the IIS Administration tool, navigating to the right area, and adding the IP through the UI.
Why not just keep the bad IPs in a database table and check against that table from Application_BeginRequest?
That is a great idea, and also something I do, but it turns out that not every request goes through Application_BeginRequest. A malicious request like the one in red above would not go through Application_BeginRequest. A user flooding your server with these requests can easily affect server performance if the requests are allowed through, which is why I’ve outlined additional steps.
What actually happens when you block an IP?
Now that your IP is added to the block list in applicationHost.config, what does a hacker from that IP see when accessing your website? Unfortunately blocking via applicationHost.config does not block the hacker’s attempt at connecting at all. It still lets the hacker content but the hacker will see this in his web browser:
Another thing to note is that “dangerous” requests don’t go down the same pipeline that normal requests do, and therefore “dangerous” requests circumvent the IP blocking in applicationHost.config altogether. A dangerous request will still be responded to with this IIS error:
System.Web.HttpException (0x80004005): A potentially dangerous Request.Path value
was detected from the client (<). at System.Web.HttpRequest.ValidateInputIfRequiredByConfig() at
System.Web.HttpApplication.PipelineStepManager.ValidateHelper(HttpContext context)
As an example, here’s a “dangerous” request you can try now on the GMass website: http://www.gmass.co/booya/build_now%3Csvg/onload=alert(1337)%3E
So, even if your IP was blocked, you would still be able to access that dangerous URL and you would still get the same message.
Send incredible emails & automations and avoid the spam folder — all in one powerful but easy-to-learn tool
TRY GMASS FOR FREE
Download Chrome extension - 30 second install!
No credit card required