閱讀英文

共用方式為


ASP.NET Core 的用戶端 IP 安全清單

作者:Damien BowdenTom Dykstra

本文說明在 ASP.NET Core 應用程式中實作 IP 位址安全清單 (也稱為允許清單) 的三種方式。 隨附的範例應用程式會示範這三種方法。 您可以使用:

  • 中介軟體用以檢查每個要求的遠端 IP 位址。
  • MVC 動作篩選條件用以檢查特定控制器或動作方法之要求的遠端 IP 位址。
  • Razor Pages 篩選條件用以檢查 Razor Pages 之要求的遠端 IP 位址。

在每個案例中,包含已核准用戶端 IP 位址的字串會儲存在應用程式設定中。 中介軟體或篩選條件:

  • 將字串剖析為陣列。
  • 檢查陣列中是否存在遠端 IP 位址。

如果陣列包含 IP 位址,則允許存取。 否則會傳回 HTTP 403 禁止狀態碼。

檢視或下載範例程式碼 \(英文\) (如何下載)

IP 位址安全清單

在範例應用程式中,IP 位址安全清單為:

JSON
{
  "AdminSafeList": "127.0.0.1;192.168.1.5;::1",
  "Logging": {

在上述範例中,允許 127.0.0.1192.168.1.5 的 IPv4 位址,以及 ::1 的 IPv6 回送位址(0:0:0:0:0:0:0:1 的壓縮格式)。

中介軟體

Startup.Configure 方法會將自訂 AdminSafeListMiddleware 中介軟體類型新增至應用程式的要求管線。 安全清單是透過 .NET Core 組態提供者擷取,並以建構函式參數的形式傳遞。

C#
app.UseMiddleware<AdminSafeListMiddleware>(Configuration["AdminSafeList"]);

中介軟體會將字串剖析為陣列,並搜尋陣列中的遠端 IP 位址。 如果找不到遠端 IP 位址,中介軟體會傳回 HTTP 403 禁止。 HTTP GET 要求會略過此驗證程序。

C#
public class AdminSafeListMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<AdminSafeListMiddleware> _logger;
    private readonly byte[][] _safelist;

    public AdminSafeListMiddleware(
        RequestDelegate next,
        ILogger<AdminSafeListMiddleware> logger,
        string safelist)
    {
        var ips = safelist.Split(';');
        _safelist = new byte[ips.Length][];
        for (var i = 0; i < ips.Length; i++)
        {
            _safelist[i] = IPAddress.Parse(ips[i]).GetAddressBytes();
        }

        _next = next;
        _logger = logger;
    }

    public async Task Invoke(HttpContext context)
    {
        if (context.Request.Method != HttpMethod.Get.Method)
        {
            var remoteIp = context.Connection.RemoteIpAddress;
            _logger.LogDebug("Request from Remote IP address: {RemoteIp}", remoteIp);

            var bytes = remoteIp.GetAddressBytes();
            var badIp = true;
            foreach (var address in _safelist)
            {
                if (address.SequenceEqual(bytes))
                {
                    badIp = false;
                    break;
                }
            }

            if (badIp)
            {
                _logger.LogWarning(
                    "Forbidden Request from Remote IP address: {RemoteIp}", remoteIp);
                context.Response.StatusCode = (int) HttpStatusCode.Forbidden;
                return;
            }
        }

        await _next.Invoke(context);
    }
}

動作篩選條件

如果您想要針對特定 MVC 控制器或動作方法進行安全清單驅動的存取控制,請使用動作篩選條件。 例如:

C#
public class ClientIpCheckActionFilter : ActionFilterAttribute
{
    private readonly ILogger _logger;
    private readonly string _safelist;

    public ClientIpCheckActionFilter(string safelist, ILogger logger)
    {
        _safelist = safelist;
        _logger = logger;
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var remoteIp = context.HttpContext.Connection.RemoteIpAddress;
        _logger.LogDebug("Remote IpAddress: {RemoteIp}", remoteIp);
        var ip = _safelist.Split(';');
        var badIp = true;
        
        if (remoteIp.IsIPv4MappedToIPv6)
        {
            remoteIp = remoteIp.MapToIPv4();
        }
        
        foreach (var address in ip)
        {
            var testIp = IPAddress.Parse(address);
            
            if (testIp.Equals(remoteIp))
            {
                badIp = false;
                break;
            }
        }

        if (badIp)
        {
            _logger.LogWarning("Forbidden Request from IP: {RemoteIp}", remoteIp);
            context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
            return;
        }

        base.OnActionExecuting(context);
    }
}

Startup.ConfigureServices 中,將動作篩選條件新增至 MVC 篩選條件集合。 在下列範例中,新增了 ClientIpCheckActionFilter 動作篩選條件。 安全清單和主控台記錄器執行個體會以建構函式參數的形式傳遞。

C#
services.AddScoped<ClientIpCheckActionFilter>(container =>
{
    var loggerFactory = container.GetRequiredService<ILoggerFactory>();
    var logger = loggerFactory.CreateLogger<ClientIpCheckActionFilter>();

    return new ClientIpCheckActionFilter(
        Configuration["AdminSafeList"], logger);
});
C#
services.AddScoped<ClientIpCheckActionFilter>(_ =>
{
    var logger = _loggerFactory.CreateLogger<ClientIpCheckActionFilter>();
    
    return new ClientIpCheckActionFilter(
        Configuration["AdminSafeList"], logger);
});

然後可使用 [ServiceFilter] 屬性,將此動作篩選條件套用至控制器或動作方法:

C#
[ServiceFilter(typeof(ClientIpCheckActionFilter))]
[HttpGet]
public IEnumerable<string> Get()

在範例應用程式中,動作篩選條件會套用至控制器的 Get 動作方法。 當您傳送下列項目來測試應用程式時:

  • HTTP GET 要求,[ServiceFilter] 屬性會驗證用戶端 IP 位址。 如果允許存取 Get 動作方法,則動作篩選條件和動作方法會產生下列主控台輸出的變化:

    dbug: ClientIpSafelistComponents.Filters.ClientIpCheckActionFilter[0]
          Remote IpAddress: ::1
    dbug: ClientIpAspNetCore.Controllers.ValuesController[0]
          successful HTTP GET    
    
  • 除了 GET 以外的 HTTP 要求指定動詞,AdminSafeListMiddleware 中介軟體會驗證用戶端 IP 位址。

Razor Pages 篩選條件

如果您想要對 Razor Pages 應用程式進行安全清單驅動的存取控制,請使用 Razor Pages 篩選條件。 例如:

C#
public class ClientIpCheckPageFilter : IPageFilter
{
    private readonly ILogger _logger;
    private readonly IPAddress[] _safelist;

    public ClientIpCheckPageFilter(
        string safelist,
        ILogger logger)
    {
        var ips = safelist.Split(';');
        _safelist = new IPAddress[ips.Length];
        for (var i = 0; i < ips.Length; i++)
        {
            _safelist[i] = IPAddress.Parse(ips[i]);
        }

        _logger = logger;
    }

    public void OnPageHandlerExecuting(PageHandlerExecutingContext context)
    {
        var remoteIp = context.HttpContext.Connection.RemoteIpAddress;
        if (remoteIp.IsIPv4MappedToIPv6)
        {
            remoteIp = remoteIp.MapToIPv4();
        }
        _logger.LogDebug(
            "Remote IpAddress: {RemoteIp}", remoteIp);

        var badIp = true;
        foreach (var testIp in _safelist)
        {
            if (testIp.Equals(remoteIp))
            {
                badIp = false;
                break;
            }
        }

        if (badIp)
        {
            _logger.LogWarning(
                "Forbidden Request from Remote IP address: {RemoteIp}", remoteIp);
            context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
            return;
        }
    }

    public void OnPageHandlerExecuted(PageHandlerExecutedContext context)
    {
    }

    public void OnPageHandlerSelected(PageHandlerSelectedContext context)
    {
    }
}

Startup.ConfigureServices 中,將 Razor Pages 篩選條件新增至 MVC 篩選條件集合加以啟用。 在下列範例中,新增了 ClientIpCheckPageFilterRazor Pages 篩選條件。 安全清單和主控台記錄器執行個體會以建構函式參數的形式傳遞。

C#
services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        var logger = LoggerFactory.Create(builder => builder.AddConsole())
                        .CreateLogger<ClientIpCheckPageFilter>();
        var filter = new ClientIpCheckPageFilter(
            Configuration["AdminSafeList"], logger);
        
        options.Filters.Add(filter);
    });
C#
services.AddMvc(options =>
{
    var logger = _loggerFactory.CreateLogger<ClientIpCheckPageFilter>();
    var clientIpCheckPageFilter = new ClientIpCheckPageFilter(
        Configuration["AdminSafeList"], logger);
    
    options.Filters.Add(clientIpCheckPageFilter);
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

若要求了範例應用程式的 IndexRazor 頁面,Razor Pages 篩選條件會驗證用戶端 IP 位址。 篩選條件會產生下列主控台輸出的變化:

dbug: ClientIpSafelistComponents.Filters.ClientIpCheckPageFilter[0]
      Remote IpAddress: ::1

其他資源