Protección de APIs en dotnet 6 con una clave compartida (forma elegante)
Proteger las APIs es una tarea crítica en el desarrollo de software moderno. Ya sea un sistema interno o externo, los datos que se comparten deben estar protegidos. Una forma de proteger las APIs es mediante el uso de claves compartidas, que permiten la autenticación interna entre sistemas. En este artículo, veremos cómo proteger las APIs de dotnet 6 con una clave compartida utilizando el código proporcionado.
El código proporcionado en el artículo es una clase C# llamada PreSharedKeyAuthenticationHandler, que hereda de la clase AuthenticationHandler. El propósito de esta clase es autenticar la solicitud en función de una clave precompartida que se pasa como encabezado en la solicitud.
public class PreSharedKeyAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private const string PreSharedKeyHeader = "X-PreSharedKey";
private readonly string _preSharedKey;
public PreSharedKeyAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{
_preSharedKey = Environment.GetEnvironmentVariable("API_SHARED_KEY") ?? "";
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.TryGetValue(PreSharedKeyHeader, out var preSharedKeyValues))
{
return Task.FromResult(AuthenticateResult.Fail("Pre-shared key header is missing."));
}
var preSharedKey = preSharedKeyValues.ToString();
if (preSharedKey != _preSharedKey)
{
return Task.FromResult(AuthenticateResult.Fail("Invalid pre-shared key."));
}
var identity = new ClaimsIdentity("PreSharedKeyAuthentication");
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, "PreSharedKeyAuthentication");
return Task.FromResult(AuthenticateResult.Success(ticket));
}
}
La primera etapa es generar una clave compartida. Esta clave debe ser una cadena compleja que sea difícil de adivinar. Una vez que se genera la clave, debe almacenarse de forma segura, como en una variable de entorno. En el código proporcionado, se recupera la clave compartida de la variable de entorno “API_SHARED_KEY”. Si la variable de entorno no está definida, se utiliza una cadena vacía como clave compartida.
Una vez que se genera y almacena la clave compartida, el siguiente paso es crear la clase PreSharedKeyAuthenticationHandler. Esta clase es responsable de manejar la autenticación de la solicitud. El constructor de esta clase toma varios parámetros, incluyendo las opciones para el esquema de autenticación, un logger factory, un encoder y un reloj del sistema. El parámetro más importante es el parámetro de opciones, que proporciona la configuración para el esquema de autenticación.
El método HandleAuthenticateAsync es el corazón de la clase PreSharedKeyAuthenticationHandler. Este método se llama cuando se invoca el manejador de autenticación. Lo primero que hace el método es verificar si el encabezado de clave precompartida está presente en la solicitud. Si falta el encabezado, se devuelve un resultado de autenticación fallido (401).
Si el encabezado de clave precompartida está presente, el código compara el valor del encabezado con el valor de la clave compartida que se recuperó anteriormente de la variable de entorno. Si los dos valores coinciden, se devuelve un resultado de autenticación exitoso. Si los dos valores no coinciden, se devuelve un resultado de autenticación fallido.
Si la autenticación es exitosa, se crea una ClaimsIdentity con el nombre “PreSharedKeyAuthentication”. A continuación, se crea un ClaimsPrincipal con la identidad y se crea un AuthenticationTicket con el principal y el nombre “PreSharedKeyAuthentication”. Finalmente, se devuelve un resultado de autenticación exitoso con el ticket de autenticación.
Recuerde generar la clave compartida y almacenarla de forma segura, y registrar la clase PreSharedKeyAuthenticationHandler en el esquema de autenticación de la aplicación de la siguiente manera:
En su archivo Program.cs agregue lo siguiente:
// Configurar autenticación de clave precompartida
builder.Services.AddAuthentication("PreSharedKeyAuthentication")
.AddScheme("PreSharedKeyAuthentication", null);
Para usar el esquema de autenticación en su controlador, puede decorar sus métodos o la clase del controlador con el atributo [Authorize] y especificar el esquema de autenticación que se va a utilizar como “PreSharedKeyAuthentication”. Aquí hay un controller de ejemplo:
[ApiController]
[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = "PreSharedKeyAuthentication")]
public class MyProtectedController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok("Este es un endpoint protegido.");
}
}
Para probar la autenticación, se puede usar una herramienta como Curl para realizar una solicitud con el encabezado de clave compartida adecuado. Aquí hay un comando curl de ejemplo:
curl -H “X-PreSharedKey: TU_CLAVE_COMPARTIDA” http://localhost:5000/api/myprotected
Reemplace “YOUR_SHARED_KEY” con el valor real de la clave compartida. Si la clave es correcta, la respuesta debería ser “Este es un punto final protegido”. Si la clave es incorrecta o falta, la respuesta será un error de autenticación.
En conclusión, proteger APIs con claves compartidas es un paso importante para la seguridad de la aplicación. Mediante el uso de la clase PreSharedKeyAuthenticationHandler proporcionada en este artículo, se puede implementar fácilmente la autenticación de clave compartida en APIs de dotnet 6, esta autenticación se recomienda para sistemas internos.