ASP.NET MVCでControllerに対するIPアドレス制限

ちょっとASP.NET MVCのController単位でIPアドレス制限してみるテスト(・∀・)
とりあえずIPアドレスチェックするクラスをこんなカンジで。

// IPアドレスヘルパー
public static class IPAddressHelper
{
    // addressがsubnet/mask内か判定
    public static bool IsAddressInSubnet(IPAddress address, IPAddress subnet, IPAddress mask)
    {
        byte[] addressBytes = address.GetAddressBytes();
        byte[] maskBytes = mask.GetAddressBytes();
        byte[] maskedAddressBytes = new byte[ addressBytes.Length ];

        for( int i = 0; i < maskedAddressBytes.Length; ++i )
        {
            maskedAddressBytes[ i ] = (byte)( addressBytes[ i ] & maskBytes[ i ] );
        }

        IPAddress maskedAddress = new IPAddress( maskedAddressBytes );

        return subnet.Equals( maskedAddress );
    }

    // サブネットマスク取得
    public static IPAddress GetMaskIpAddress(int cidr)
    {
        switch( cidr )
        {
        case 8:
            return IPAddress.Parse( "255.0.0.0" );
..
        case 32:
            return IPAddress.Parse( "255.255.255.255" );
        default:
            return IPAddress.Parse( "255.255.255.255" );
        }
    }
}

去年、同じようなもののJava版を作りましたが、今回はネットからコピペ(´д`;)


そして、チェック用のIPアドレス一覧を保持するクラスをこんなカンジで。
これは、後述のIPFilterAttributeで毎回IPアドレス一覧を作るのを省略するためだけのもの。

// サブネット/マスクリストのキャッシュ
public class IPFilter
{
    // subnet/maskのペア
    private class AddressPair
    {
        public IPAddress Subnet;
        public IPAddress Mask;
    }

    // キャッシュ
    private static readonly Dictionary<string, IPFilter> filters = new Dictionary<string, IPFilter>();

    // IPアドレス一覧
    private readonly List<AddressPair> allowList = new List<AddressPair>();

    // キャッシュ取得
    public static IPFilter Get(string addresses)
    {
        lock( filters )
        {
            IPFilter filter;

            if( !filters.TryGetValue( addresses, out filter ) )
            {
                filter = new IPFilter( addresses.Split( ',' ) );
                filters[ addresses ] = filter;
            }

            return filter;
        }
    }

    // IPアドレス一覧構築
    public IPFilter(params string[] ipAddresses)
    {
        foreach( string ipAddress in ipAddresses )
        {
            int cidr = 32;
            int find = ipAddress.IndexOf( '/' );
            if( find != -1 )
            {
                cidr = Convert.ToInt32( ipAddress.Substring( find + 1 ) );
            }

            AddressPair address = new AddressPair();
            address.Subnet = IPAddress.Parse( find == -1 ? ipAddress : ipAddress.Substring( 0, find ) );
            address.Mask = IPAddressHelper.GetMaskIpAddress( cidr );
            this.allowList.Add( address );
        }
    }

    // 一覧からの存在チェック
    public bool IsExist(string ipAddress)
    {
        foreach( AddressPair address in allowList )
        {
            if( IPAddressHelper.IsAddressInSubnet( IPAddress.Parse( ipAddress ), address.Subnet, address.Mask ) )
            {
                return true;
            }
        }

        return false;
    }
}

っで、ASP.NET MVCのフィルタをこんなカンジで(・ω・)

// IPアドレスフィルタ
[AttributeUsage( AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true )]
public class IPFilterAttribute : FilterAttribute, IAuthorizationFilter
{
    private IPFilter ipFilter;

    public IPFilterAttribute(string configKey)
    {
        string addresses = ConfigurationManager.AppSettings[ configKey ];
        if( addresses != null )
        {
            this.ipFilter = IPFilter.Get( addresses );
        }
    }

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        if( this.ipFilter != null )
        {
            if( !this.ipFilter.IsExist( filterContext.HttpContext.Request.UserHostAddress ) )
            {
                throw new HttpException( 403, "Forbidden" );
            }
        }
    }
}

そして使い方(・∀・)

[IPFilter( "allowIPAddress" )]
public class RestrictController : Controller
{
...
}
// Web.config
<configuration>

  <appSettings>
    <add key="allowIPAddress" value="10.0.0.0/8,127.0.0.1"/>
  </appSettings>

...
</configuration>

これでIPアドレス制限できたかな(・∀・)?