ASP.NET Core 的客户端 IP 安全安全

作者: Damien BowdenTom Dykstra

本文介绍三种在 ASP.NET Core 应用程序中实现 IP 安全列表(也称为白名单)的方法。 你可以使用:

  • 用于检查每个请求的远程 IP 地址的中间件。
  • 操作筛选器来检查针对特定控制器或操作方法的请求的远程 IP 地址。
  • Razor Pages 筛选器来检查 Razor 页面的请求的远程 IP 地址。

在每种情况下,包含批准的客户端 IP 地址的字符串存储在应用设置中。 中间件或筛选器会将字符串分析为一个列表,并检查远程 IP 是否在列表中。 如果不是,则返回 HTTP 403 禁止的状态代码。

查看或下载示例代码如何下载

安全安全

appsettings文件中配置该列表。 它是以分号分隔的列表,并且可以包含 IPv4 和 IPv6 地址。

{
  "AdminSafeList": "127.0.0.1;192.168.1.5;::1",
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

中间件

Configure 方法将添加中间件,并在构造函数参数中将安全项的字符串传递给它。

public void Configure(
    IApplicationBuilder app, 
    IHostingEnvironment env, 
    ILoggerFactory loggerFactory)
{
    loggerFactory.AddNLog();

    app.UseStaticFiles();

    app.UseMiddleware<AdminSafeListMiddleware>(Configuration["AdminSafeList"]);
    app.UseMvc();
}

中间件将字符串分析为数组,并在数组中查找远程 IP 地址。 如果找不到远程 IP 地址,中间件将返回 HTTP 401 禁止访问。 对于 HTTP Get 请求,将跳过此验证过程。

public class AdminSafeListMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<AdminSafeListMiddleware> _logger;
    private readonly string _adminSafeList;

    public AdminSafeListMiddleware(
        RequestDelegate next, 
        ILogger<AdminSafeListMiddleware> logger, 
        string adminSafeList)
    {
        _adminSafeList = adminSafeList;
        _next = next;
        _logger = logger;
    }

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

            string[] ip = _adminSafeList.Split(';');

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

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

        await _next.Invoke(context);
    }
}

操作筛选器

如果只希望特定控制器或操作方法使用 "安全",请使用操作筛选器。 以下是一个示例:

using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace ClientIpAspNetCore.Filters
{
    public class ClientIpCheckFilter : ActionFilterAttribute
    {
        private readonly ILogger _logger;
        private readonly string _safelist;

        public ClientIpCheckFilter
            (ILoggerFactory loggerFactory, IConfiguration configuration)
        {
            _logger = loggerFactory.CreateLogger("ClientIdCheckFilter");
            _safelist = configuration["AdminSafeList"];
        }

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            var remoteIp = context.HttpContext.Connection.RemoteIpAddress;
            _logger.LogInformation(
                "Remote IpAddress: {RemoteIp}", remoteIp);

            string[] ip = _safelist.Split(';');

            var badIp = true;
            foreach (var address in ip)
            {
                if (remoteIp.IsIPv4MappedToIPv6)
                {
                    remoteIp = remoteIp.MapToIPv4();
                }
                var testIp = IPAddress.Parse(address);
                if (testIp.Equals(remoteIp))
                {
                    badIp = false;
                    break;
                }
            }

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

            base.OnActionExecuting(context);
        }
    }
}

操作筛选器将添加到服务容器中。

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<ClientIpCheckFilter>();

    services.AddMvc(options =>
    {
        options.Filters.Add
            (new ClientIpCheckPageFilter
                (_loggerFactory, Configuration));
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

然后,可以在控制器或操作方法上使用该筛选器。

[ServiceFilter(typeof(ClientIpCheckFilter))]
[HttpGet]
public IEnumerable<string> Get()

在示例应用中,筛选器将应用于 Get 方法。 因此,当你通过发送 Get API 请求来测试应用时,属性将验证客户端 IP 地址。 当你通过使用任何其他 HTTP 方法调用 API 进行测试时,中间件将验证客户端 IP。

Razor Pages 筛选器

如果需要 Razor Pages 应用的安全安全,请使用 Razor Pages 筛选器。 以下是一个示例:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Net;

namespace ClientIpAspNetCore
{
    public class ClientIpCheckPageFilter : IPageFilter
    {
        private readonly ILogger _logger;
        private readonly string _safelist;

        public ClientIpCheckPageFilter
            (ILoggerFactory loggerFactory, IConfiguration configuration)
        {
            _logger = loggerFactory.CreateLogger("ClientIdCheckPageFilter");
            _safelist = configuration["AdminSafeList"];
        }

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

            string[] ip = _safelist.Split(';');

            var badIp = true;
            foreach (var address in ip)
            {
                if (remoteIp.IsIPv4MappedToIPv6)
                {
                    remoteIp = remoteIp.MapToIPv4();
                }
                var testIp = IPAddress.Parse(address);
                if (testIp.Equals(remoteIp))
                {
                    badIp = false;
                    break;
                }
            }

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

        public void OnPageHandlerExecuted(PageHandlerExecutedContext context)
        {
        }

        public void OnPageHandlerSelected(PageHandlerSelectedContext context)
        {
        }
    }
}

此筛选器通过将其添加到 MVC 筛选器集合来启用。

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<ClientIpCheckFilter>();

    services.AddMvc(options =>
    {
        options.Filters.Add
            (new ClientIpCheckPageFilter
                (_loggerFactory, Configuration));
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

当你运行应用并请求 Razor 页面时,Razor Pages 筛选器将验证客户端 IP。

后续步骤

了解有关 ASP.NET Core 中间件的详细信息

上一篇:在 ASP.NET Core 中使用 SameSite cookie

下一篇:ASP.NET Core 身份验证概述

关注微信小程序
程序员编程王-随时随地学编程

扫描二维码
程序员编程王

扫一扫关注最新编程教程