Ler en inglés

Compartir por


Lista segura de IP de cliente para ASP.NET Core

Por Damien Bowden y Tom Dykstra

En este artículo se muestran tres maneras de implementar una lista segura de direcciones IP (también conocida como lista de permitidos) en una aplicación ASP.NET Core. Una aplicación de ejemplo complementaria muestra los tres enfoques. Puede usar:

  • Middleware para comprobar la dirección IP remota de cada solicitud.
  • La acción de MVC filtra para comprobar la dirección IP remota de las solicitudes de controladores o métodos de acción específicos.
  • Razor Pages cuenta con filtros para verificar la dirección IP remota de las solicitudes para Razor Pages.

En cada caso, una cadena que contiene direcciones IP de cliente aprobadas se almacena en una configuración de aplicación. Middleware o filtro:

  • Analiza la cadena en una matriz.
  • Comprueba si la dirección IP remota existe en la matriz.

Se permite el acceso si la matriz contiene la dirección IP. De lo contrario, se devuelve un código de estado HTTP 403 Prohibido.

Vea o descargue el código de ejemplo (cómo descargarlo)

Lista segura de direcciones IP

En la aplicación de ejemplo, la lista segura de direcciones IP es:

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

En el ejemplo anterior, se permiten las direcciones IPv4 de 127.0.0.1 y 192.168.1.5 y la dirección IPv6 loopback de (::1formato comprimido para 0:0:0:0:0:0:0:1).

Software intermedio

El Startup.Configure método agrega el tipo de middleware personalizado AdminSafeListMiddleware a la canalización de solicitudes de la aplicación. La lista segura se recupera con el proveedor de configuración de .NET Core y se pasa como parámetro de constructor.

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

El middleware analiza la cadena en una matriz y busca la dirección IP remota en la matriz. Si no se encuentra la dirección IP remota, el middleware devuelve HTTP 403 Prohibido. Este proceso de validación se omite para las solicitudes 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);
    }
}

Filtro de acciones

Si desea un control de acceso controlado por listas seguras para controladores o métodos de acción específicos de MVC, use un filtro de acción. Por ejemplo:

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

En Startup.ConfigureServices, añade el filtro de acción a la colección de filtros MVC. En el ejemplo siguiente, se agrega un ClientIpCheckActionFilter filtro de acción. Una lista segura y una instancia del registrador de consola se pasan como parámetros de constructor.

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

A continuación, el filtro de acciones se puede aplicar a un controlador o método de acción con el atributo [ServiceFilter]:

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

En la aplicación de ejemplo, el filtro de acción se aplica al método de acción del Get controlador. Al probar la aplicación mediante el envío de:

  • Una solicitud HTTP GET, el [ServiceFilter] atributo valida la dirección IP del cliente. Si se permite el acceso al Get método de acción, el filtro de acción y el método de acción generan una variación de la siguiente salida de la consola:

    dbug: ClientIpSafelistComponents.Filters.ClientIpCheckActionFilter[0]
          Remote IpAddress: ::1
    dbug: ClientIpAspNetCore.Controllers.ValuesController[0]
          successful HTTP GET    
    
  • Un verbo de solicitud HTTP distinto de GET, el AdminSafeListMiddleware middleware valida la dirección IP del cliente.

Razor Filtro de Pages

Si desea un control de acceso controlado por listas seguras para una Razor aplicación Pages, use un Razor filtro Pages. Por ejemplo:

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)
    {
    }
}

En Startup.ConfigureServices, habilite el Razor filtro Pages agregándolo a la colección de filtros MVC. En el ejemplo siguiente, se agrega un ClientIpCheckPageFilterRazor filtro de acción. Una lista segura y una instancia del registrador de consola se pasan como parámetros de constructor.

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

Cuando se solicita la página ÍndiceRazor de la aplicación de ejemplo, el Razor filtro Pages valida la dirección IP del cliente. El filtro produce una variación de la siguiente salida de consola:

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

Recursos adicionales