Condividi tramite


Elenco di indirizzi IP client sicuri per ASP.NET Core

Di Damien Bowden e Tom Dykstra

Questo articolo illustra tre modi per implementare un elenco indirizzi IP safelist (noto anche come elenco di indirizzi consentiti) in un'app ASP.NET Core. Un'app di esempio associata illustra tutti e tre gli approcci. Puoi usare:

  • Middleware per controllare l'indirizzo IP remoto di ogni richiesta.
  • Filtri di azione MVC per controllare l'indirizzo IP remoto delle richieste per controller o metodi di azione specifici.
  • Razor Le pagine filtrano per controllare l'indirizzo IP remoto delle richieste per Razor le pagine.

In ogni caso, una stringa contenente indirizzi IP client approvati viene archiviata in un'impostazione dell'app. Middleware o filtro:

  • Analizza la stringa in una matrice.
  • Controlla se l'indirizzo IP remoto esiste nella matrice.

L'accesso è consentito se la matrice contiene l'indirizzo IP. In caso contrario, viene restituito un codice di stato HTTP 403 Non consentito.

Visualizzare o scaricare il codice di esempio (procedura per il download)

Elenco di indirizzi IP sicuri

Nell'app di esempio l'elenco indirizzi IP safelist è:

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

Nell'esempio precedente sono consentiti gli indirizzi IPv4 di 127.0.0.1 e 192.168.1.5 e l'indirizzo di loopback IPv6 ( ::1 formato compresso per 0:0:0:0:0:0:0:1).

Middleware

Il Startup.Configure metodo aggiunge il tipo middleware personalizzato AdminSafeListMiddleware alla pipeline di richiesta dell'app. L'elenco di sicurezza viene recuperato con il provider di configurazione .NET Core e viene passato come parametro del costruttore.

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

Il middleware analizza la stringa in una matrice e cerca l'indirizzo IP remoto nella matrice. Se l'indirizzo IP remoto non viene trovato, il middleware restituisce HTTP 403 Accesso negato. Questo processo di convalida viene ignorato per le richieste HTTP GET.

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 azioni

Se si desidera un controllo di accesso basato su safelist per specifici controller MVC o metodi di azione, usare un filtro di azione. Ad esempio:

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

In Startup.ConfigureServicesaggiungere il filtro azione all'insieme di filtri MVC. Nell'esempio seguente viene aggiunto un filtro azione ClientIpCheckActionFilter . Un elenco sicuro e un'istanza del logger della console vengono passati come parametri del costruttore.

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

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

Il filtro azione può quindi essere applicato a un controller o a un metodo di azione con l'attributo [ServiceFilter] :

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

Nell'app di esempio il filtro azione viene applicato al metodo di azione del Get controller. Quando si testa l'app inviando:

  • Una richiesta HTTP GET, l'attributo [ServiceFilter] convalida l'indirizzo IP del client. Se l'accesso al Get metodo di azione è consentito, viene generata una variante dell'output della console seguente dal metodo action filter e action:

    dbug: ClientIpSafelistComponents.Filters.ClientIpCheckActionFilter[0]
          Remote IpAddress: ::1
    dbug: ClientIpAspNetCore.Controllers.ValuesController[0]
          successful HTTP GET    
    
  • Un verbo di richiesta HTTP diverso da GET, il AdminSafeListMiddleware middleware convalida l'indirizzo IP del client.

Razor Filtro pagine

Se si vuole un controllo di accesso basato su elenchi sicuri per un'app Razor Pages, usare un Razor filtro Pages. Ad esempio:

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

In Startup.ConfigureServicesabilitare il Razor filtro Pages aggiungendolo all'insieme di filtri MVC. Nell'esempio seguente viene aggiunto un ClientIpCheckPageFilterRazor filtro Pages. Un elenco sicuro e un'istanza del logger della console vengono passati come parametri del costruttore.

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

Quando viene richiesta la pagina IndiceRazor dell'app di esempio, il Razor filtro Pages convalida l'indirizzo IP del client. Il filtro produce una variante dell'output della console seguente:

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

Risorse aggiuntive