Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Blocking IIS IP Addresses with ASP.NET


:P
On this page:

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

Posted in .NET  ASP.NET  CSharp  IIS  IIS  Tools  

The Voices of Reason


 

Doug Dodge
April 28, 2007

# re: Blocking IIS IP Addresses with ASP.NET

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

Jeff Atwood
April 28, 2007

# re: Blocking IIS IP Addresses with ASP.NET


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.

Rick Strahl
April 29, 2007

# re: Blocking IIS IP Addresses with ASP.NET

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...

Jon T
May 01, 2007

# re: Blocking IIS IP Addresses with ASP.NET

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

Rick Strahl
May 01, 2007

# re: Blocking IIS IP Addresses with ASP.NET

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
June 23, 2007

# Rick Strahl's Web Log


Ciz
September 11, 2007

# re: Blocking IIS IP Addresses with ASP.NET

Great stuff Rick. I also like the HttpModule approach...
http://www.codeproject.com/useritems/http-module-ip-security.asp

Feryt
November 18, 2007

# re: Blocking IIS IP Addresses with ASP.NET

Good job!
Thanx a lot.
F.

Mike
February 07, 2008

# re: Blocking IIS IP Addresses with ASP.NET

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

whitesites
February 28, 2008

# re: Blocking IIS IP Addresses with ASP.NET

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.

WhiteSites
March 01, 2008

# re: Blocking IIS IP Addresses with ASP.NET

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.

whitesites
March 01, 2008

# re: Blocking IIS IP Addresses with ASP.NET

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!

Edwin Matos
March 12, 2008

# re: Blocking IIS IP Addresses with ASP.NET

This code is gold for me. Nice job Rick.

hilton smith
May 29, 2008

# re: Blocking IIS IP Addresses with ASP.NET

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.

ecards
May 29, 2008

# re: Blocking IIS IP Addresses with ASP.NET

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();

ecards
May 30, 2008

# re: Blocking IIS IP Addresses with ASP.NET

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

David
August 20, 2008

# re: Blocking IIS IP Addresses with ASP.NET

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.

Dan
July 16, 2009

# re: Blocking IIS IP Addresses with ASP.NET

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.

Rajkumar
August 03, 2009

# re: Blocking IIS IP Addresses with ASP.NET

When i try to acess the IIS metabase i get the following error.
The RPC server is unavailable.

Dono how to solve the error?

BuzzAnn
September 09, 2009

# re: Blocking IIS IP Addresses with ASP.NET

Install the IP Security role service to manage in IIS 7 via the IIS Manager.
http://www.iis.net/ConfigReference/system.webServer/security/ipSecurity

Rajkumar
September 22, 2009

# re: Blocking IIS IP Addresses with ASP.NET

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.

sunboyzhe
May 08, 2010

# re: Blocking IIS IP Addresses with ASP.NET

This code seems great for adding blocked IPs, but is there any way to remove the blocks?

FLR
May 11, 2010

# re: Blocking IIS IP Addresses with ASP.NET

Does the addition of IP addresses to the deny list add any processing overhead?

MB
November 26, 2010

# It's possible in IIS7, too

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

skywalker
December 24, 2010

# re: Blocking IIS IP Addresses with ASP.NET

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

Luke
August 29, 2011

# re: Blocking IIS IP Addresses with ASP.NET

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?

paultechguy
April 22, 2012

# re: Blocking IIS IP Addresses with ASP.NET

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.

Roger
June 28, 2012

# re: Blocking IIS IP Addresses with ASP.NET

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"

Todd
June 10, 2015

# re: Blocking IIS IP Addresses with ASP.NET

Hi Rick - is the West Wind component for blocking IIS IP Address with ASP.NET still available? We have a couple of old W2K3 servers that house some newer projects, and we notice that the Russians have been picking at it. We would like to block the entire range from the Russian Federation, and probably some others like China, etc. I ran across your project, but I don't know which component from WestWind to use? Thanks!

Todd

Rick Strahl
June 10, 2015

# re: Blocking IIS IP Addresses with ASP.NET

@Todd, there's no 'tool'. The code for this is in the post above so you can add this yourself to a page. Alternately if you need help I can probably help you get this set up, but should be pretty straight forward. The APIs changed in IIS7 and later, but the same principles can still be applied.

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