如何手动解密 ASP.NET Core 身份验证 cookie? [英] How to manually decrypt an ASP.NET Core Authentication cookie?

查看:50
本文介绍了如何手动解密 ASP.NET Core 身份验证 cookie?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们考虑一个众所周知的 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/"),自动身份验证 = 真,自动挑战 = 真});//...}

然后序列化一个主体:

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

在这两次调用之后,一个加密的 cookie 将存储在客户端.您可以在任何浏览器开发工具中看到 cookie(在我的情况下它是分块的):

使用来自应用程序代码的 cookie 不是问题(也不是问题).

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

我检查了 docs 并发现只有常见的词:

<块引用>

这将创建一个加密的 cookie 并将其添加到当前回复.配置时指定的 AuthenticationScheme 必须也可在调用 SignInAsync 时使用.

在幕后使用的加密是 ASP.NET 的数据保护系统.如果您在多台机器上托管,负载平衡或使用网络农场,那么您将需要配置数据保护以使用相同的密钥环和应用程序标识符.

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

更新 #1:基于 Ron C 很好的答案和评论,我最终得到了代码:

公共类启动{//构造函数被省略...public void ConfigureServices(IServiceCollection 服务){services.AddDataProtection().PersistKeysToFileSystem(new DirectoryInfo(@"C:	emp-keys"));服务.AddMvc();}公共无效配置(IApplicationBuilder 应用程序){app.UseCookieAuthentication(new CookieAuthenticationOptions(){AuthenticationScheme = "MyCookie",CookieName = "MyCookie",LoginPath = new PathString("/Home/Index/"),AccessDeniedPath = new PathString("/Home/AccessDenied/"),自动身份验证 = 真,自动挑战 = 真});app.UseStaticFiles();app.UseMvcWithDefaultRoute();}}公共类 HomeController : 控制器{公共异步任务指数(){等待 HttpContext.Authentication.SignInAsync("MyCookie", new ClaimsPrincipal());返回视图();}公共 IActionResult DecryptCookie(){var provider = DataProtectionProvider.Create(new DirectoryInfo(@"C:	emp-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);字符串纯文本 = specialUtf8Encoding.GetString(plainBytes);返回内容(纯文本);}}

不幸的是,这段代码总是在 Unprotect 方法调用时产生异常:

<块引用>

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

我在多台机器上测试了这段代码的不同变体,但没有得到肯定的结果.可能我犯了一个错误,但在哪里?

更新 #2: 我的错误是在 UseCookieAuthentication 中没有设置 DataProtectionProvider.再次感谢@RonC.

解决方案

无需密钥即可解密身份验证 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");

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

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

public IActionResult DecryptCookie() {//获取加密后的cookie值string cookieValue = HttpContext.Request.Cookies["MyCookie"];//获取数据保护器以用于任一方法var dataProtector = provider.CreateProtector(typeof(CookieAuthenticationMiddleware).FullName, "MyCookie", "v2");//获取解密后的cookie为纯文本UTF8Encoding specialUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);byte[] protectedBytes = Base64UrlTextEncoder.Decode(cookieValue);byte[] plainBytes = dataProtector.Unprotect(protectedBytes);字符串纯文本 = specialUtf8Encoding.GetString(plainBytes);//获取解密后的cookie作为身份验证票TicketDataFormat ticketDataFormat = new TicketDataFormat(dataProtector);AuthenticationTicket 票证 = ticketDataFormat.Unprotect(cookieValue);返回视图();}

此方法使用一个名为 providerIDataProtectionProvider,它是构造函数注入的.


将密钥保存到目录时解密身份验证 Cookie

如果您想在应用程序之间共享 cookie,那么您可能决定将数据保护密钥保存到一个目录中.这可以通过将以下内容添加到 startup.cs 文件的 ConfigureServices 方法来完成:

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

请小心,因为密钥未加密,因此您需要保护它们!!!只有在绝对必要时才将密钥保存到目录中(或者如果您只是想了解系统的工作原理).您将需要指定一个使用这些键的 cookie DataProtectionProvider.这可以在 startup.cs 类的 Configure 方法中的 UseCookieAuthentication 配置的帮助下完成,如下所示:

app.UseCookieAuthentication(new CookieAuthenticationOptions() {DataProtectionProvider = DataProtectionProvider.Create(new DirectoryInfo(@"C:	emp-keys")),AuthenticationScheme = "MyCookie",CookieName = "MyCookie",LoginPath = new PathString("/Home/Login"),AccessDeniedPath = new PathString("/Home/AccessDenied"),自动身份验证 = 真,自动挑战 = 真});

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

 public IActionResult DecryptCookie() {ViewData["Message"] = "这是解密页面";var user = HttpContext.User;//用户将被设置为 ClaimsPrincipal//获取加密后的cookie值string cookieValue = HttpContext.Request.Cookies["MyCookie"];var provider = DataProtectionProvider.Create(new DirectoryInfo(@"C:	emp-keys"));//获取数据保护器以用于任一方法var dataProtector = provider.CreateProtector(typeof(CookieAuthenticationMiddleware).FullName, "MyCookie", "v2");//获取解密后的cookie为纯文本UTF8Encoding specialUtf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);byte[] protectedBytes = Base64UrlTextEncoder.Decode(cookieValue);byte[] plainBytes = dataProtector.Unprotect(protectedBytes);字符串纯文本 = specialUtf8Encoding.GetString(plainBytes);//获取解密的cookies作为身份验证票TicketDataFormat ticketDataFormat = new TicketDataFormat(dataProtector);AuthenticationTicket 票证 = ticketDataFormat.Unprotect(cookieValue);返回视图();}

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

Let's consider a common-known ASP.NET Core scenario. Firstly we add the 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
    });
    //...
}

Then serialize a principal:

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

After these two calls an encrypted cookie will be stored at the client side. You can see the cookie (in my case it was chunked) in any browser devtools:

It's not a problem (and not a question) to work with cookies from application code.

My question is: how to decrypt the cookie outside the application? I guess a private key is needed for that, how to get it?

I checked the docs and found only common words:

This will create an encrypted cookie and add it to the current response. The AuthenticationScheme specified during configuration must also be used when calling SignInAsync.

Under the covers the encryption used is ASP.NET's Data Protection system. If you are hosting on multiple machines, load balancing or using a web farm then you will need to configure data protection to use the same key ring and application identifier.

So, is it possible to decrypt the authentication cookie, and if so how?

UPDATE #1: Based on Ron C great answer and comments, I've ended up with code:

public class Startup
{
    //constructor is omitted...
    
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDataProtection().PersistKeysToFileSystem(
            new DirectoryInfo(@"C:	emp-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:	emp-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);
    }
}

Unfortunately this code always produces exception on Unprotect method call:

CryptographicException in Microsoft.AspNetCore.DataProtection.dll: Additional information: The payload was invalid.

I tested different variations of this code on several machines without positive result. Probably I made a mistake, but where?

UPDATE #2: My mistake was the DataProtectionProvider hasn't been set in UseCookieAuthentication. Thanks to @RonC again.

解决方案

Decrypting the Authentication Cookie without needing the keys

It's worth noting that you don't need to gain access to the keys to decrypt the authentication cookie. You simply need to use the right IDataProtector created with the right purpose parameter, and subpurpose parameters.

Based on the CookieAuthenticationMiddleware source code https://github.com/aspnet/Security/blob/rel/1.1.1/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationMiddleware.cs#L4 it looks like the purpose you need to pass is typeof(CookieAuthenticationMiddleware). And since they are passing additional parameters to the IDataProtector you will need to match them. So this line of code should get you an IDataProtector that can be used to decrypt the authentication cookie:

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

Note thatOptions.AuthenticationScheme is just "MyCookie" in this case since that's what it was set to in the Configure method of the startup.cs file.

Here is an example action method for decrypting your authentication cookie two different ways:

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();
}

This method uses an IDataProtectionProvider called provider that is constructor injected.


Decrypting the Authentication Cookie when persisting keys to a directory

If you want to share cookies between applications then you might decide to persist the data protection keys to a directory. This can be done by adding the following to the ConfigureServices method of the startup.cs file:

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

BE CAREFUL though because the keys are not encrypted so it's up to you to protect them!!! Only persist the keys to a directory if you absolutely must, (or if you are just trying to understand how the system works). You will also need to specify a cookie DataProtectionProvider that uses those keys. This can be done with the help of the UseCookieAuthentication configuration in the Configure method of the startup.cs class like so:

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

With that configuration done. You can now decrypt the authentication cookie with the following code:

 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:	emp-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();
    }

You can learn more about this latter scenario here: https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/compatibility/cookie-sharing

这篇关于如何手动解密 ASP.NET Core 身份验证 cookie?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆