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.