security authentication cookies asp.net-core asp.net-core-mvc

security - ¿Cómo descifrar manualmente una cookie de autenticación de ASP.NET Core?



authentication cookies (3)

Descifrar la cookie de autenticación sin necesidad de las claves

Vale la pena señalar que no necesita obtener acceso a las claves para descifrar la cookie de autenticación. Simplemente necesita usar el IDataProtector correcto creado con el parámetro de propósito correcto, y los parámetros de propósito secundario.

Basado en el código fuente CookieAuthenticationMiddleware https://github.com/aspnet/Security/blob/rel/1.1.1/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationMiddleware.cs#L4 parece el propósito que necesita para pasar es typeof(CookieAuthenticationMiddleware) . Y dado que están pasando parámetros adicionales al IDataProtector , deberá hacerlos coincidir. Entonces, esta línea de código debería proporcionarle un IDataProtector que se puede utilizar para descifrar la cookie de autenticación:

var dataProtector = provider.CreateProtector(typeof(CookieAuthenticationMiddleware).FullName, Options.AuthenticationScheme, "v2");

Tenga en cuenta que Options.AuthenticationScheme es solo "MyCookie" en este caso, ya que eso es lo que se configuró en el método Configure del archivo startup.cs.

Aquí hay un método de acción de ejemplo para descifrar su cookie de autenticación de dos maneras diferentes:

public IActionResult DecryptCookie() { //Get the encrypted cookie value string cookieValue = HttpContext.Request.Cookies["MyCookie"]; //Get a data protector to use with either approach var dataProtector = provider.CreateProtector(typeof(CookieAuthenticationMiddleware).FullName, "MyCookie", "v2"); //Get the decrypted cookie as plain text UTF8Encoding specialUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); byte[] protectedBytes = Base64UrlTextEncoder.Decode(cookieValue); byte[] plainBytes = dataProtector.Unprotect(protectedBytes); string plainText = specialUtf8Encoding.GetString(plainBytes); //Get the decrypted cookie as a Authentication Ticket TicketDataFormat ticketDataFormat = new TicketDataFormat(dataProtector); AuthenticationTicket ticket = ticketDataFormat.Unprotect(cookieValue); return View(); }

Este método utiliza un provider llamado IDataProtectionProvider que es inyectado por el constructor.


Descifrar la cookie de autenticación cuando persiste las claves de un directorio

Si desea compartir cookies entre aplicaciones, puede optar por conservar las claves de protección de datos en un directorio. Esto se puede hacer agregando lo siguiente al método ConfigureServices del archivo startup.cs:

services.AddDataProtection().PersistKeysToFileSystem( new DirectoryInfo(@"C:/temp-keys/"));

Tenga cuidado porque las claves no están encriptadas, ¡así que depende de usted protegerlas! Solo conserve las claves de un directorio si es absolutamente necesario (o si solo está tratando de entender cómo funciona el sistema). También deberá especificar una cookie DataProtectionProvider que use esas claves. Esto se puede hacer con la ayuda de la configuración UseCookieAuthentication en el método Configure de la clase startup.cs de la siguiente manera:

app.UseCookieAuthentication(new CookieAuthenticationOptions() { DataProtectionProvider = DataProtectionProvider.Create(new DirectoryInfo(@"C:/temp-keys/")), AuthenticationScheme = "MyCookie", CookieName = "MyCookie", LoginPath = new PathString("/Home/Login"), AccessDeniedPath = new PathString("/Home/AccessDenied"), AutomaticAuthenticate = true, AutomaticChallenge = true });

Con esa configuración hecha. Ahora puede descifrar la cookie de autenticación con el siguiente código:

public IActionResult DecryptCookie() { ViewData["Message"] = "This is the decrypt page"; var user = HttpContext.User; //User will be set to the ClaimsPrincipal //Get the encrypted cookie value string cookieValue = HttpContext.Request.Cookies["MyCookie"]; var provider = DataProtectionProvider.Create(new DirectoryInfo(@"C:/temp-keys/")); //Get a data protector to use with either approach var dataProtector = provider.CreateProtector(typeof(CookieAuthenticationMiddleware).FullName, "MyCookie", "v2"); //Get the decrypted cookie as plain text UTF8Encoding specialUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); byte[] protectedBytes = Base64UrlTextEncoder.Decode(cookieValue); byte[] plainBytes = dataProtector.Unprotect(protectedBytes); string plainText = specialUtf8Encoding.GetString(plainBytes); //Get teh decrypted cookies as a Authentication Ticket TicketDataFormat ticketDataFormat = new TicketDataFormat(dataProtector); AuthenticationTicket ticket = ticketDataFormat.Unprotect(cookieValue); return View(); }

Puede obtener más información sobre este último escenario aquí: https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/compatibility/cookie-sharing

Consideremos un escenario conocido de ASP.NET Core. Primero agregamos el middleware:

public void Configure(IApplicationBuilder app) { app.UseCookieAuthentication(new CookieAuthenticationOptions() { AuthenticationScheme = "MyCookie", CookieName = "MyCookie", LoginPath = new PathString("/Home/Login/"), AccessDeniedPath = new PathString("/Home/AccessDenied/"), AutomaticAuthenticate = true, AutomaticChallenge = true }); //... }

Luego serialice un director:

await HttpContext.Authentication.SignInAsync("MyCookie", principal);

Después de estas dos llamadas, se almacenará una cookie cifrada en el lado del cliente. Puede ver la cookie (en mi caso fue fragmentada) en las herramientas del navegador:

No es un problema (ni una pregunta) trabajar con cookies del código de la aplicación.

Mi pregunta es: ¿cómo descifrar la cookie fuera de la aplicación ? Supongo que se necesita una clave privada para eso, ¿cómo obtenerla?

Revisé los docs y encontré solo palabras comunes:

Esto creará una cookie encriptada y la agregará a la respuesta actual. El AuthenticationScheme especificado durante la configuración también debe usarse al llamar a SignInAsync.

Debajo de las cubiertas, el cifrado utilizado es el sistema de protección de datos de ASP.NET. Si hospeda en varias máquinas, equilibra la carga o usa una granja de servidores web, deberá configurar la protección de datos para usar el mismo llavero e identificador de la aplicación.

Entonces, ¿es posible descifrar la cookie de autenticación y, de ser así, cómo?

ACTUALIZACIÓN # 1: Basado en la excelente respuesta y comentarios de Ron C, terminé con el código:

public class Startup { //constructor is omitted... public void ConfigureServices(IServiceCollection services) { services.AddDataProtection().PersistKeysToFileSystem( new DirectoryInfo(@"C:/temp-keys/")); services.AddMvc(); } public void Configure(IApplicationBuilder app) { app.UseCookieAuthentication(new CookieAuthenticationOptions() { AuthenticationScheme = "MyCookie", CookieName = "MyCookie", LoginPath = new PathString("/Home/Index/"), AccessDeniedPath = new PathString("/Home/AccessDenied/"), AutomaticAuthenticate = true, AutomaticChallenge = true }); app.UseStaticFiles(); app.UseMvcWithDefaultRoute(); } } public class HomeController : Controller { public async Task<IActionResult> Index() { await HttpContext.Authentication.SignInAsync("MyCookie", new ClaimsPrincipal()); return View(); } public IActionResult DecryptCookie() { var provider = DataProtectionProvider.Create(new DirectoryInfo(@"C:/temp-keys/")); string cookieValue = HttpContext.Request.Cookies["MyCookie"]; var dataProtector = provider.CreateProtector( typeof(CookieAuthenticationMiddleware).FullName, "MyCookie", "v2"); UTF8Encoding specialUtf8Encoding = new UTF8Encoding(false, true); byte[] protectedBytes = Base64UrlTextEncoder.Decode(cookieValue); byte[] plainBytes = dataProtector.Unprotect(protectedBytes); string plainText = specialUtf8Encoding.GetString(plainBytes); return Content(plainText); } }

Desafortunadamente, este código siempre produce una excepción en la llamada al método Unprotect :

CryptographicException en Microsoft.AspNetCore.DataProtection.dll: Información adicional: La carga útil no era válida.

Probé diferentes variaciones de este código en varias máquinas sin resultado positivo. Probablemente cometí un error, pero ¿dónde?

ACTUALIZACIÓN # 2: Mi error fue que DataProtectionProvider no se ha configurado en UseCookieAuthentication . Gracias a @RonC nuevamente.


Otra variación para ASP.NET Core 2.2:

var cookieManager = new ChunkingCookieManager(); var cookie = cookieManager.GetRequestCookie(HttpContext, ".AspNetCore.Identity.Application"); var dataProtector = dataProtectionProvider.CreateProtector("Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", "Identity.Application", "v2"); //Get the decrypted cookie as plain text UTF8Encoding specialUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); byte[] protectedBytes = Base64UrlTextEncoder.Decode(cookie); byte[] plainBytes = dataProtector.Unprotect(protectedBytes); string plainText = specialUtf8Encoding.GetString(plainBytes); //Get teh decrypted cookies as a Authentication Ticket TicketDataFormat ticketDataFormat = new TicketDataFormat(dataProtector); AuthenticationTicket ticket = ticketDataFormat.Unprotect(cookie);


Vea a continuación un método auxiliar para .NET Core 2 para obtener reclamos de una cookie:

private IEnumerable<Claim> GetClaimFromCookie(HttpContext httpContext, string cookieName, string cookieSchema) { // Get the encrypted cookie value var opt = httpContext.RequestServices.GetRequiredService<IOptionsMonitor<CookieAuthenticationOptions>>(); var cookie = opt.CurrentValue.CookieManager.GetRequestCookie(httpContext, cookieName); // Decrypt if found if (!string.IsNullOrEmpty(cookie)) { var dataProtector = opt.CurrentValue.DataProtectionProvider.CreateProtector("Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", cookieSchema, "v2"); var ticketDataFormat = new TicketDataFormat(dataProtector); var ticket = ticketDataFormat.Unprotect(cookie); return ticket.Principal.Claims; } return null; }

Como señaló @Cirem, la forma poco fiable de crear un protector es exactamente como lo hace Microsoft (vea su código aquí ). Por lo tanto, puede cambiar en futuras versiones.