Contact   •   Products   •   Search

Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs

Blocking IIS IP Addresses with ASP.NET


Over the last few months I've had increasing SPAM traffic coming to my site, usually from a few select IP address groups. Most of this site spamming crap that I actually catch tries to simply POST to every possible URL on the site apparently that contains a FORM tag, spewing forth a plethora of links. Now typically this isn't much of a problem because the POST operatoins tend to fail in ASP.NET either with a ViewState violation or more commonly dangerous request content which is simply refused.

However, I do see the content in my error logs which report any errors that occur in various applications and while I could simply ignore ViewState and RequestValidation errors  I prefer not to have to look at this SPAM crap.

So I broke down and created a little application to start adding IP Addresses to the IIS restriction list and until recently I used this list manually. The list is now something like 500 addresses long and it's cut back significantly on the amount of POST spam that comes into the site as a whole. While it may seem like a lot of this stuff is coming from random IP addresses much of it appears comes from a repeating pool of addresses so with a bit of diligence it's possible to make a dent in this.

It's certainly not an end all solution as IP spoofing can be used to generate a never ending supply of IP addresses, but this simple approach has resulted in a significant cutbackof the offending traffic.

Blocking IP Addresses

Blocking IP Addresses in IIS involves setting up an IP Deny list, which if you're using IIS 6 can be set up from the Directory Security | IP Address and Domain Name Restrictions dialog. You can basically set up IP addresses to allow or restrict:

 

The focus for what I needed to do is block IP addresses so you set all computers have access and the create individual Deny entries for each IP Address or IP Address block to deny.

To deny a block of addresses you need to apply Subnet Masks - the mask basicly allows telling how many addresses in a subnet block to block. A Subnet mask of 255.255.255.255 maps exactly one IP address. While 255.255.255.0 will map an entire block when using 192.168.1.* (the 0 in the subnet mask basically says no mask on the final block so include all 255 entries).

Internally IIS stores these values in IPSecurity section of the metabase/configuration where it stores both the IP Address and subnet mask. For example:

192.168.1.101,255.255.255.255
64.44.123.0,255.255.255.0

where the first will block exactly one IP address, where the latter will block all in 64.44.123.*.

Interesting note on IIS 7 - it appears that the IP Address restriction list is not available through the IIS UI any longer - any additions to this interface have to be made programmatically or via the command line tool. I couldn't even find my entries in the ApplicationHost.config file - not quite sure where these values are actually stored.

Creating a quick and dirty Web based Tool 

You can get at this info with the IISAdmin objects quite easily and in order to work with this I created a class to handle the logic to retireve a list of blocked IP Addresses and to set it in a more simplified fashion with a Web interface.

The idea is that I can quickly view and add IP Addresses to the list and I can copy the list between the various Web sites on this server simply by cutting and pasting. Again this isn't an end all solution but I've had a number of occasions where blocking several IPAddresses quickly (usually with a Robot gone wild) can stop a log jam on the server.

The key code to do this is the class below that handles the interaction with the IP Security functionality in IIS. Note that you'll need to run as an Admin or SYSTEM in order to be able to execute any of this code (more on this below).

 

/// <summary>
/// Allows reading and setting the IIS Blocked IP List for a given Web site
/// 
/// Note:
/// These routines only handle simple IP addressing rules. It handles
/// the IPDeny list only and works only with full IP Addresses or
/// Subnet blocked wildcard IP Addresses. If you use domain name
/// lookups don't use these routines to update!
/// 
/// These routines require ADMIN or System level permissions in
/// order to set IIS configuration settings.
/// 
/// Disclaimer:
/// Use at your own risk. These routines modify the IIS metabase
/// and therefore have the potential to muck up your configuration.
/// YOU ARE FULLY RESPONSIBLE FOR ANY PROBLEMS THAT THESE ROUTINES
/// MIGHT CAUSE TO YOUR CONFIGURATION!
/// </summary>
public class IISBlockedIpList : IDisposable
{
 
    public string MetaBasePath
    {
        get { return _MetaBasePath; }
        set { _MetaBasePath = value; }
    }
    private string _MetaBasePath = "IIS://localhost/W3SVC/1/ROOT";        
 
    private DirectoryEntry IIS;
 
    public IISBlockedIpList(string metaBasePath)
    {
        if (!string.IsNullOrEmpty(metaBasePath))
            this.MetaBasePath = metaBasePath;
 
    }
 
    /// <summary>
    /// Returns a list of Ips as a plain string returning just the
    /// IP Addresses, leaving out the subnet mask values.
    /// 
    /// Any wildcarded IP Addresses will return .0 for the
    /// wild card characters.
    /// </summary>
    /// <returns></returns>
    public string[] GetIpList()
    {
        this.Open();
 
        // *** Grab the IP List
        object IPSecurity = IIS.Properties["IPSecurity"].Value;
 
         // retrieve the IPDeny list from the IPSecurity object. Note: Strings as objects
         //Array origIPDenyList = (Array)wwUtils.GetPropertyCom(IPSecurity, "IPDeny");
 
        Array origIPDenyList = (Array)
         IPSecurity.GetType().InvokeMember("IPDeny",
                BindingFlags.Public |
                BindingFlags.Instance | BindingFlags.GetProperty,
                null, IPSecurity, null);
 
        this.Close();
 
        // *** Format and Extract into a string list
        List<string> Ips = new List<string>();
        foreach (string IP in origIPDenyList)
        {
            // *** Strip off the subnet-mask - we'll use .0 or .* to represent
            string TIP = IP.Substring(0, IP.IndexOf(",") ); //wwUtils.ExtractString(IP, "", ",");
            Ips.Add(TIP);
        }
 
 
        return Ips.ToArray();
    }
 
    /// <summary>
    /// Allows you to pass an array of strings that contain the IP Addresses
    /// to block. 
    /// 
    /// Wildcard IPs should use .* or .0 to indicate blocks.
    /// 
    /// Note this string list should contain ALL IP addresses to block
    /// not just new and added ones (ie. use GetList first and then
    /// add to the list.
    /// </summary>
    /// <param name="IPStrings"></param>
    public void SetIpList(string[] IPStrings)
    {
        this.Open();
 
        object IPSecurity = IIS.Properties["IPSecurity"].Value;
 
        // *** IMPORTANT: This list MUST be object or COM call will fail!
        List<object> newIpList = new List<object>();
 
        foreach (string Ip in IPStrings)
        {
            string newIp;
 
            if (Ip.EndsWith(".*.*.*") || Ip.EndsWith(".0.0.0"))
                newIp = Ip.Replace(".*", ".0") + ",255.0.0.0";
            else if (Ip.EndsWith(".*.*") || Ip.EndsWith(".0.0"))
                newIp = Ip.Replace(".*", ".0") + ",255.255.0.0";
            else if (Ip.EndsWith(".*") || Ip.EndsWith(".0"))
            {
                // *** Wildcard requires different IP Mask
                newIp = Ip.Replace(".*", ".0") + ",255.255.255.0";
            }
            else
                newIp = Ip + ", 255.255.255.255";
 
 
            // *** Check for dupes - nasty but required because
            // *** object -> string comparison can't do BinarySearch
            bool found = false;
            foreach (string tempIp in newIpList)
            {
                if (newIp == tempIp)
                {   
                    found = true;
                    break;
                }
            }
            if (!found)
                newIpList.Add(newIp);
        }
 
        //wwUtils.SetPropertyCom(this.IPSecurity, "GrantByDefault", true);
 
        IPSecurity.GetType().InvokeMember("GrantByDefault",
                BindingFlags.Public |
                BindingFlags.Instance | BindingFlags.SetProperty,
                null, IPSecurity, new object[] { true });
 
 
        object[] ipList = newIpList.ToArray();
 
        // *** Apply the new list
        //wwUtils.SetPropertyCom(this.IPSecurity, "IPDeny",ipList);
 
        IPSecurity.GetType().InvokeMember("IPDeny",
                 BindingFlags.Public |
                 BindingFlags.Instance | BindingFlags.SetProperty,
                 null, IPSecurity, new object[] { ipList });
 
 
        IIS.Properties["IPSecurity"].Value = IPSecurity;
        IIS.CommitChanges();
        IIS.RefreshCache();
 
        this.Close();
    }
 
 
    /// <summary>
    /// Adds IP Addresses to the existing IP Address list
    /// </summary>
    /// <param name="IPString"></param>
    public void AddIpList(string[] newIps)
    {
        string[] origIps = this.GetIpList();
 
        List<string> Ips = new List<string>(origIps);
        foreach (string ip in newIps)
        {
            Ips.Add(ip);
        }
 
        this.SetIpList(Ips.ToArray());
    }
 
    /// <summary>
    /// Returns a list of all IIS Sites on the server
    /// </summary>
    /// <returns></returns>
    public IIsWebSite[] GetIIsWebSites()
    {
        // *** IIS://Localhost/W3SVC/
        string iisPath = this.MetaBasePath.Substring(0,this.MetaBasePath.ToLower().IndexOf("/w3svc/")) + "/W3SVC";
        DirectoryEntry root = new DirectoryEntry(iisPath);
 
        List<IIsWebSite> Sites = new List<IIsWebSite>();
        foreach (DirectoryEntry Entry in root.Children)
        {
            System.DirectoryServices.PropertyCollection Properties = Entry.Properties;
 
            try
            {
                IIsWebSite Site = new IIsWebSite();
                Site.SiteName = (string)Properties["ServerComment"].Value;
 
                // *** Skip over non site entries
                if (Site.SiteName == null || Site.SiteName == "")
                    continue;
 
                Site.IISPath = Entry.Path;
                Sites.Add(Site);
            }
            catch { ; }
        }
 
        root.Close();
 
        return Sites.ToArray();
    }
 
    private void Open()
    {
        this.Open(this.MetaBasePath);
    }
    private void Open(string IISMetaPath)
    {
        if (this.IIS == null)
            this.IIS = new DirectoryEntry(IISMetaPath);
    }
    private void Close()
    {
        if (IIS != null)
        {
            this.IIS.Close();
            this.IIS = null;
 
        }
    }
 
    #region IDisposable Members
    public void Dispose()
    {
        if (this.IIS != null)
            IIS.Close();
    }
    #endregion
}
 
/// <summary>
/// Container class that holds information about an IIS Web site
/// </summary>
public class IIsWebSite
{
 
    /// <summary>
    /// The display name of the Web site
    /// </summary>
    public string SiteName
    {
        get { return _SiteName; }
        set { _SiteName = value; }
    }
    private string _SiteName = "";
 
    /// <summary>
    /// The IIS Metabase path
    /// </summary>
    public string IISPath
    {
        get { return _IISPath; }
        set { _IISPath = value; }
    }
    private string _IISPath = "";
 
}

This code is pretty easy to use with just a few lines of code. Here are the two methods in the ASP.NET form that use the class:

 

private void ShowBlockedIps()
{
    string[] ipDenyList = IpList.GetIpList();
    StringBuilder sb = new StringBuilder();
 
    int count = 0;
    foreach (string IP in ipDenyList)
    {
        sb.AppendLine(IP);
    }
    this.lblIpCount.Text = (ipDenyList.Length * 10).ToString() + " blocked addresses";
 
    this.txtAddresses.Text = sb.ToString();
}
 
protected void btnUpdate_Click(object sender, EventArgs e)
{
    string enteredIps = this.txtAddresses.Text;
    string[] ipStrings = enteredIps.Replace("\n", "").Split(new char[1] { '\r' }, StringSplitOptions.RemoveEmptyEntries);
    this.IpList.SetIpList(ipStrings);
 
    this.ShowBlockedIps();
}

The two functions GetIpList and SetIpList work with string arrays and simply retrieve or set the IPDenyList.

The class takes a few liberties with the formatting of the values to set - it basically allows using .* or .0 as wildcard characters which trigger the subnetmask to be updated so you can enter things like this:

192.168.1.101
201.123.123.*
201.2.*.*

There's also a method used to retrieve a list of Web sites and their Metabase paths so you can quickly switch to another site and apply the same set of IP restrictions.

Security

As mentioned above in order for any of this to work you need to make sure you run as an Administrator or under SYSTEM and that your application is running in Full Trust. Without full trust you can't access DirectoryServices/IISAdmin. So this is hardly a general purpose utility, but something you'd have to confine to an admin area of the site.

What I do is use Windows Authentication and programmatic Impersonation to force an Admin account to process these requests. I had talked about this before (to some criticism), but using programmatic Impersonation is a great way to raise the underlying permissions of a single request rather than the entire Web application (which is the only way you can do this with ASP.NET's native configuration). Programmatic Impersonation lets you essentially impersonate the logged on Windows Authentication user for just a single request without having to run the entire site with Impersonation enabled. It's pretty simple to do with this code:

 
void Impersonate()
{
    if (!string.IsNullOrEmpty(this.User.Identity.Name) && this.User.IsInRole("Administrators"))
    {
        WindowsIdentity id = this.User.Identity as WindowsIdentity;
        WindowsImpersonationContext context = id.Impersonate();
    }
 
    return;
}

Do this at the beginning of the request and off you are (as long as Windows Authentication is enabled and Full Trust is used).

More interesting Uses

The Web page interface is useful, but it would be even nicer if you could do something like this more automatically. For example, when the Web site generates a dangerous request or an Invalid ViewState error and you can check whether the requests is likely a SPAM type entry, you can automatically add entries to this list.

You probably wouldn't want to add these entries directly as part of your application because of the overhead and more importantly because of the security requirements - a regular request is DEFINITELY not going to have the rights to write into the Metabase nor should it. So what I did instead is dump out the invalid IP address requests in a text file, which I can periodically access and cut and paste into the list. This actually has made it much easier to get a list of IPs to block rather quickly.

 

You can find the source code for all of this here:
http://www.west-wind.com/files/tools/misc/IpAddressBlock.zip

Make Donation
Posted in .NET  ASP.NET  CSharp  IIS  IIS  Tools  


Feedback for this Post

 
# re: Blocking IIS IP Addresses with ASP.NET
by Doug Dodge April 28, 2007 @ 8:06pm
Rick, Don't know if this will be interesting to you but here's an interesting approach to dealing with spammers: http://www.benzedrine.cx/relaydb.html
# re: Blocking IIS IP Addresses with ASP.NET
by Jeff Atwood April 28, 2007 @ 10:01pm

This seems wrong to me.

Isn't there a more automatic way to do this? Eg, have code that looks for a threshold of (x)errors from the same IP in (y) minutes, then puts that IP in an (n) hour ban list?

Dunno, just a thought.
# re: Blocking IIS IP Addresses with ASP.NET
by Rick Strahl April 29, 2007 @ 1:26am
Jeff, it'd be great if it could be totally automated, but I have applications scattered all over my server. Some .NET some not and while most do error reporting and I get the reports of bad requests it's not all well set up to automation.

I've got a few places like the Web Log and the Web Store to automatically capture this stuff as I mentioned at the bottom of the post, but for some of the others manual entry works as well. It's a few handfuls for requests so it's not a monumental task...
# DotNetSlackers: Blocking IIS IP Addresses with ASP.NET
by DotNetSlackers Latest ASP.NET News April 29, 2007 @ 1:28am
# re: Blocking IIS IP Addresses with ASP.NET
by Jon T May 01, 2007 @ 12:52am
I'm running on IIS7 and added my ip's under system.webserver/security/ipSecurity like this:
            <ipSecurity>
                <add ipAddress="200.88.125.9" /> 
                <add ipAddress="201.80.184.239" />
                <add ipAddress="60.208.64.177" />
                <add ipAddress="130.36.87.102" />
                <add ipAddress="82.134.95.226" />
                ...
            </ipSecurity>


For details about the schema check it out here: http://www.iis.net/default.aspx?tabid=2&subtabid=25&i=946&p=40
# re: Blocking IIS IP Addresses with ASP.NET
by Rick Strahl May 01, 2007 @ 2:38am
Jon, you know it's interesting I can't for the life of me find the IP Addresses that I've blocked in applicationhost.config or in the root web sites web.config (where they should be).

But the point of this excercise is that I need to be able to do this remotely <s>

I started automatically collecting IPs now from a few of the server apps and the list is piling up quite nicely.
# Rick Strahl's Web Log
by Rick Strahl's Web Log June 23, 2007 @ 5:51pm
# re: Blocking IIS IP Addresses with ASP.NET
by Ciz September 11, 2007 @ 12:46am
Great stuff Rick. I also like the HttpModule approach...
http://www.codeproject.com/useritems/http-module-ip-security.asp
# re: Blocking IIS IP Addresses with ASP.NET
by Feryt November 18, 2007 @ 4:08pm
Good job!
Thanx a lot.
F.
# re: Blocking IIS IP Addresses with ASP.NET
by Mike February 07, 2008 @ 5:15pm
I wrote a Freeware (no cost, no trial limits) program to ban by IP, wildcard IP or entire countries.

Feel free to grab it

http://www.ontarioabandonedplaces.com/ipguardian
# re: Blocking IIS IP Addresses with ASP.NET
by whitesites February 28, 2008 @ 7:18pm
Rick, I too have noticed an increase in spammers hitting my sites. Its almost like a DOS attach as they are requesting 30+ pages / second. I have my big site throttled, so if someone trys to browse a little too quick it blacklists their IP. I just wish there was a automated way to directly add these IPs to the IIS ban list.
# re: Blocking IIS IP Addresses with ASP.NET
by WhiteSites March 01, 2008 @ 4:45pm
I have found the perfect solution. Checkout <a href="http://www.stopforumspam.com"> Stop Forum Spam</a> They have an API you can check IPs against to see if they have a history of abuse. They also have their list of over 3100 IPs you can download to implement your own block. Does anyone know of an easy way to bulk import these IPs into the IP restrictions on IIS? You can also signup for an API key to contribute to their list of bad IPs.
# re: Blocking IIS IP Addresses with ASP.NET
by whitesites March 01, 2008 @ 7:13pm
I ran into a few issues getting your IP address Blocking Application working, but its all working now. Awesome Application! Can't wait to see how this reduces server load from spammers!
# re: Blocking IIS IP Addresses with ASP.NET
by Edwin Matos March 12, 2008 @ 7:01am
This code is gold for me. Nice job Rick.
# re: Blocking IIS IP Addresses with ASP.NET
by hilton smith May 29, 2008 @ 3:51am
used this code in a slightly modified fashion to auto ban ip's that generate x amount of 404's inside of a timelimit. it works great. the only thing i'm battling with is adding a specific range to be banned.
# re: Blocking IIS IP Addresses with ASP.NET
by ecards May 29, 2008 @ 5:12pm
This code seems great for adding blocked IPs, but is there any way to remove the blocks?

Any idea why the code below won't clear the Deny list?

Regards,
LTG

iisAdmin = new DirectoryEntry(MetaBasePath);
object ipSecurity = iisAdmin.Properties["IPSecurity"].Value;
Type ipSecurityType = ipSecurity.GetType();

object[] emptyArray = new object[] { };
ipSecurityType.InvokeMember("IPDeny",
BindingFlags.SetProperty,
null, ipSecurity, new object[] { emptyArray });

iisAdmin.Properties["IPSecurity"].Value = ipSecurity;
iisAdmin.CommitChanges();
iisAdmin.RefreshCache();
# re: Blocking IIS IP Addresses with ASP.NET
by ecards May 30, 2008 @ 10:04am
Ok, I rewrote all my code and got it to work.

I've created an app the automatically reads known IP Feeds so with a couple clicks you can block all Chinese IP addresses for example. You can also add your own lists.

Any feedback appreciated:
http://www.hdgreetings.com/ecards/block-ip-iis/

Regards,
LTG
# re: Blocking IIS IP Addresses with ASP.NET
by David August 20, 2008 @ 2:04pm
Great tool. I combined your solution with the instant generation of ACL's at countryipblocks.net. As they update their database daily I subscribed to their service, plugged it into some of your code and now my blocks are refreshed with new data every 24 hours.

I am using www.countryipblocks.net to block China, Korea, Russia and Turkey. Spam has dropped by 98% and hacking attempts have all but disappeared.
# re: Blocking IIS IP Addresses with ASP.NET
by Dan July 16, 2009 @ 9:40am
this looks great. I can certainly see a relatively simple way to automate it.

I like the idea of importing a block list
but I think I'll trap the errors and log those "naughty" IP addresses as well.
Once it hits a certain threshold I'll automate adding them to the block list.

It would be more useful I think if I could do it at the NIC level or somewhere in the firewall or RRAS. Any ideas on how to do this? I could then trap and block bad IP's for and from a variety of sources.
# re: Blocking IIS IP Addresses with ASP.NET
by Rajkumar August 03, 2009 @ 4:56am
When i try to acess the IIS metabase i get the following error.
The RPC server is unavailable.

Dono how to solve the error?
# re: Blocking IIS IP Addresses with ASP.NET
by BuzzAnn September 09, 2009 @ 1:52pm
Install the IP Security role service to manage in IIS 7 via the IIS Manager.
http://www.iis.net/ConfigReference/system.webServer/security/ipSecurity
# re: Blocking IIS IP Addresses with ASP.NET
by Rajkumar September 22, 2009 @ 3:05am
HI,

How to block Ip address by using CIDR notations.

ie. How to find all the range of IP address specified in a CIDR notation and block them.
# re: Blocking IIS IP Addresses with ASP.NET
by sunboyzhe May 08, 2010 @ 7:09pm
This code seems great for adding blocked IPs, but is there any way to remove the blocks?
# re: Blocking IIS IP Addresses with ASP.NET
by FLR May 11, 2010 @ 8:11am
Does the addition of IP addresses to the deny list add any processing overhead?
# It's possible in IIS7, too
by MB November 26, 2010 @ 5:48am
But you have to install a service role: Look at this web page:

Restricting Website Access By IP Address in IIS7 - IPRestriction Module

http://omensblog.blogspot.com/2010/02/restricting-website-access-by-ip.html
# re: Blocking IIS IP Addresses with ASP.NET
by skywalker December 24, 2010 @ 9:18am
Better way for IIS 7 is to save the IPs directly in a config file, without messing with metadatabase. For easy maintenance config file can be split with configSource so that IPSecure section can be saved into it's own file.

http://boseca.blogspot.com/2010/12/programmatically-addremove-ip-security.html
# re: Blocking IIS IP Addresses with ASP.NET
by Luke August 29, 2011 @ 2:25pm
Will adding deny entries to the metabase like this cause the application pool to reset? When we add entries manually using IIS7 if you were in the middle of a session on one of the websites then your session is lost and you have to start over...

Is there any way to block an ip without clearing out the sessions?
# re: Blocking IIS IP Addresses with ASP.NET
by paultechguy April 22, 2012 @ 3:14pm
Still can't figure out how to remove all/any IPs already in the list. Even method by LTG which I've seen elsewhere doesn't appear to work.
# re: Blocking IIS IP Addresses with ASP.NET
by Roger June 28, 2012 @ 11:52am
I would also really like to use this, but I also get
The RPC Service is unavailable
:(
any help or guidance is apreciated. yes the service is running in "Services"
 


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2014