如何手动解密ASP.NET核心身份验证cookie?

Ily*_*kov 26 security authentication cookies asp.net-core-mvc asp.net-core

让我们考虑一个常见的ASP.NET Core场景.首先我们添加中间件:

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
    });
    //...
}
Run Code Online (Sandbox Code Playgroud)

然后序列化一个委托人:

await HttpContext.Authentication.SignInAsync("MyCookie", principal);
Run Code Online (Sandbox Code Playgroud)

在这两次调用之后,加密的cookie将存储在客户端.你可以在任何浏览器devtools中看到cookie(在我的例子中它是分块的):

在此输入图像描述

使用应用程序代码中的cookie不是问题(而不是问题).

我的问题是:如何在应用程序外解密cookie?我想这需要一个私钥,如何获得它?

我检查了文档,发现只有常用词:

这将创建一个加密的cookie并将其添加到当前响应中.调用SignInAsync时,还必须使用配置期间指定的AuthenticationScheme.

在封面下,使用的加密是ASP.NET的数据保护系统.如果您在多台计算机上进行托管,负载平衡或使用Web场,则需要配置数据保护以使用相同的密钥环和应用程序标识符.

那么,是否可以解密身份验证cookie,如果是,如何解密?

更新#1: 根据Ron C 的回答和评论,我最终得到了代码:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,此代码总是在Unprotect方法调用上产生异常

Microsoft.AspNetCore.DataProtection.dll中的CryptographicException:附加信息:有效内容无效.

我在几台机器上测试了这些代码的不同变体而没有正面结果.可能我犯了一个错误,但在哪里?

更新#2:我的错误是DataProtectionProvider尚未设置UseCookieAuthentication.再次感谢@RonC.

Ron*_*n C 28

在不需要密钥的情况下解密身份验证Cookie

值得注意的是,您不需要访问密钥来解​​密身份验证cookie.您只需使用使用IDataProtector 正确目的参数创建的权限和子目的参数.

基于CookieAuthenticationMiddleware源代码https://github.com/aspnet/Security/blob/rel/1.1.1/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationMiddleware.cs#L4,看起来你需要传递的目的是typeof(CookieAuthenticationMiddleware).由于他们将额外的参数传递给IDataProtector你,你需要匹配它们.因此,这行代码应该IDataProtector可以用来解密身份验证cookie:

var dataProtector = provider.CreateProtector(typeof(CookieAuthenticationMiddleware).FullName, Options.AuthenticationScheme, "v2");
Run Code Online (Sandbox Code Playgroud)

请注意,Options.AuthenticationScheme在这种情况下只是"MyCookie",因为它是Configure在startup.cs文件的方法中设置的.

以下是两种不同方式解密身份验证Cookie的示例操作方法:

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();
}
Run Code Online (Sandbox Code Playgroud)

此方法使用被注入的构造函数的IDataProtectionProvider调用provider.


将密钥持久保存到目录时解密验证Cookie

如果要在应用程序之间共享cookie,则可能决定将数据保护密钥保留在目录中.这可以通过将以下内容添加到ConfigureServicesstartup.cs文件的方法来完成:

services.AddDataProtection().PersistKeysToFileSystem(
        new DirectoryInfo(@"C:\temp-keys\")); 
Run Code Online (Sandbox Code Playgroud)

请小心,因为密钥没有加密,所以由你来保护它们!如果你绝对必须,只将密钥保存到目录中(或者如果你只是想了解系统的工作原理).您需要指定DataProtectionProvider使用这些密钥的cookie .这可以通过startup.cs类UseCookieAuthenticationConfigure方法中的配置来完成,如下所示:

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
    });
Run Code Online (Sandbox Code Playgroud)

完成配置.您现在可以使用以下代码解密身份验证cookie:

 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();
    }
Run Code Online (Sandbox Code Playgroud)

您可以在此处了解有关后一种情况的更多信息:https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/compatibility/cookie-sharing

  • 看起来在Core 2.0中,CookieAuthenticationMiddleware类已被替换,但仍然使用"Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware"的字符串值.https://github.com/aspnet/Security/blob/a7bf561b1c76aaee3d68c7448d780bc925ee283e/src/Microsoft.AspNetCore.Authentication.Cookies/PostConfigureCookieAuthenticationOptions.cs#L38 (7认同)
  • @RonC 感谢您的回复。我可能读错了,但是这个页面可能表明这是可能的?https://docs.microsoft.com/en-us/aspnet/core/security/cookie-sharing?view=aspnetcore-3.1#share-authentication-cookies- Between-aspnet-4x-and-aspnet-core-apps (2认同)

Ale*_*lex 25

在 ASP.NET Core 应用程序中,您可以只使用CookieAuthenticationOptions.TicketDataFormat.Unprotect(cookieValue).

在这里,我编写了一个简单的静态(!)方法:

public static AuthenticationTicket DecryptAuthCookie(HttpContext httpContext)
{
    // ONE - grab the CookieAuthenticationOptions instance
    var opt = httpContext.RequestServices
        .GetRequiredService<IOptionsMonitor<CookieAuthenticationOptions>>()
        .Get(CookieAuthenticationDefaults.AuthenticationScheme); //or use .Get("Cookies")

    // TWO - Get the encrypted cookie value
    var cookie = opt.CookieManager.GetRequestCookie(httpContext, opt.Cookie.Name);

    // THREE - decrypt it
    return opt.TicketDataFormat.Unprotect(cookie);
}
Run Code Online (Sandbox Code Playgroud)

在 .NET 5 和 .NET 6 下运行良好。

我添加这个答案以供参考,因为如果您搜索如何手动解密 ASP.NET auth cookie,每个搜索引擎都会弹出这个问题。

  • 对于 ASP.NET Core Identity,必须使用 .Get("Identity.Application") 而不是 .Get(CookieAuthenticationDefaults.AuthenticationScheme); (4认同)
  • 这对我来说已经达到了第二步。第三步之后我得到了 NULL,这是我没想到的。我对想法持开放态度。 (2认同)

Ale*_*aus 9

请参见下面的.NET Core 2帮助程序方法,以从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;
}
Run Code Online (Sandbox Code Playgroud)

正如@Cirem所指出的那样,创建保护程序的狡猾方法正是Microsoft的操作方式(请参阅此处的代码)。因此,它可能会在将来的版本中更改。

  • 它对我来说效果很好,并且似乎比接受的答案更容易使用。非常感谢。 (2认同)

Adi*_*iqq 8

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);
Run Code Online (Sandbox Code Playgroud)

  • @Pancake 可以在构造函数 IDataProtectionProvider dataProtectionProvider 中使用 DI 注入。您还需要将其添加到服务中:https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?view=aspnetcore-3.1#persistkeystofilesystem (2认同)