Identificar red local y habilitar 2FA con C# en .NET

Perfecto. A continuación, te dejo una solución profesional y reutilizable para .NET 8: un middleware que detecta si el cliente proviene de una red local y, si no, marca el request como “requiere 2FA” o “requiere CAPTCHA”.

También se puede usar en un filtro de acción, si prefieres aplicar la lógica solo en controladores específicos.


OBJETIVO

  • Detectar si la IP del cliente está en una red de confianza.
  • Marcar el request como requiere seguridad adicional.
  • Usar esto en endpoints REST API para tomar decisiones (ej. pedir 2FA o CAPTCHA).

Middleware global (ideal para APIs públicas)

1. Instala dependencia para validar rangos CIDR:

dotnet add package IPNetwork2


2. Crea el middleware

// Middlewares/TrustedNetworkMiddleware.cs
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using NetTools;

public class TrustedNetworkMiddleware
{
    private readonly RequestDelegate _next;
    private readonly List<IPAddressRange> _trustedRanges;

    public TrustedNetworkMiddleware(RequestDelegate next, IEnumerable<string> trustedCidrs)
    {
        _next = next;
        _trustedRanges = trustedCidrs.Select(IPAddressRange.Parse).ToList();
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var ip = ObtenerIpCliente(context);

        if (ip is not null && EsIpConfiable(ip))
        {
            context.Items["EsRedConfiable"] = true;
        }
        else
        {
            context.Items["EsRedConfiable"] = false;
        }

        await _next(context);
    }

    private static IPAddress? ObtenerIpCliente(HttpContext context)
    {
        var forwarded = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
        if (!string.IsNullOrEmpty(forwarded) && IPAddress.TryParse(forwarded, out var ipF))
            return ipF;

        return context.Connection.RemoteIpAddress;
    }

    private bool EsIpConfiable(IPAddress ip)
    {
        return _trustedRanges.Any(r => r.Contains(ip));
    }
}


3. Extensión para Program.cs

public static class TrustedNetworkMiddlewareExtensions
{
    public static IApplicationBuilder UseTrustedNetworkDetection(this IApplicationBuilder app, IEnumerable<string> trustedCidrs)
    {
        return app.UseMiddleware<TrustedNetworkMiddleware>(trustedCidrs);
    }
}


4. En Program.cs (antes de MapControllers)

var app = builder.Build();

var redesConfiables = new[]
{
    "192.168.0.0/16",
    "10.0.0.0/8",
    "172.16.0.0/12",
    "TU_RED_CORPORATIVA_PUBLICA/24"
};

app.UseTrustedNetworkDetection(redesConfiables);


5. Uso en tus controladores

[HttpPost("login")]
public IActionResult Login([FromBody] LoginRequest request)
{
    var esRedConfiable = HttpContext.Items["EsRedConfiable"] as bool? ?? false;

    if (esRedConfiable)
    {
        // Saltar CAPTCHA o 2FA
        return Ok(new { token = GenerarJwt() });
    }

    // Requiere paso adicional
    return Ok(new { requiere2fa = true, requiereCaptcha = true });
}


Alternativa: Filtro de acción para ciertos endpoints

Úsalo si solo quieres aplicar esta lógica en controladores específicos:

public class SoloRedConfiableAttribute : ActionFilterAttribute
{
    private readonly List<IPAddressRange> _trustedRanges;

    public SoloRedConfiableAttribute(string[] trustedCidrs)
    {
        _trustedRanges = trustedCidrs.Select(IPAddressRange.Parse).ToList();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var ip = context.HttpContext.Connection.RemoteIpAddress;
        var esConfiable = ip != null && _trustedRanges.Any(r => r.Contains(ip));
        if (!esConfiable)
        {
            context.Result = new ForbidResult(); // o Unauthorized, según el caso
        }
    }
}

Uso:

[HttpPost("solo-red-local")]
[ServiceFilter(typeof(SoloRedConfiableAttribute), Arguments = new object[] { new[] { "192.168.0.0/16" } })]
public IActionResult SoloRedLocal()
{
    return Ok("Estás en red confiable");
}