web-dev-qa-db-ja.com

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/"),
        AutomaticAuthenticate = true,
        AutomaticChallenge = true
    });
    //...
}

次に、プリンシパルをシリアル化します。

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

これらの2つの呼び出しの後、暗号化されたCookieがクライアント側に保存されます。ブラウザのdevtoolsでCookie(私の場合はチャンクされた)を見ることができます:

enter image description here

アプリケーションコードからCookieを操作することは問題ではありません(質問でもありません)。

私の質問は:アプリケーション外のCookieを復号化する方法?そのためには秘密鍵が必要だと思いますが、どうやって取得するのですか?

docs を確認し、一般的な単語のみを見つけました。

これにより、暗号化されたCookieが作成され、現在の応答に追加されます。構成中に指定されたAuthenticationSchemeは、SignInAsyncを呼び出すときにも使用する必要があります。

カバーの下で使用される暗号化は、ASP.NETのデータ保護システムです。複数のマシンでホストしている場合、負荷分散またはWebファームを使用している場合、同じキーリングとアプリケーション識別子を使用するようにデータ保護を構成する必要があります。

では、認証Cookieを復号化することは可能ですか?

UPDATE#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);
    }
}

残念ながら、このコードはUnprotectメソッド呼び出しで常に例外を生成します。

Microsoft.AspNetCore.DataProtection.dllのCryptographicException:追加情報:ペイロードが無効でした。

このコードのさまざまなバリエーションをいくつかのマシンでテストしましたが、良い結果は得られませんでした。おそらく間違いを犯しましたが、どこで?

UPDATE#2:私の間違いは、DataProtectionProviderUseCookieAuthenticationが設定されていないことでした。再び@RonCに感謝します。

24
Ilya Chumakov

キーを必要としない認証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に追加のパラメーターを渡すので、それらを一致させる必要があります。したがって、次のコード行により、認証Cookieの復号化に使用できるIDataProtectorが取得されます。

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

ご了承くださいOptions.AuthenticationSchemeは、startup.csファイルのConfigureメソッドで設定されているため、この場合は単に「MyCookie」です。

認証Cookieを2つの異なる方法で復号化するアクションメソッドの例を次に示します。

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

このメソッドは、コンストラクター注入されるIDataProtectionProviderと呼ばれるproviderを使用します。


キーをディレクトリに永続化する際の認証Cookieの復号化

アプリケーション間でCookieを共有する場合、データ保護キーをディレクトリに保持することを決定できます。これは、startup.csファイルのConfigureServicesメソッドに次を追加することで実行できます。

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

注意してくださいキーは暗号化されていないため、保護するのはあなた次第です!!!絶対に必要な場合(またはシステムの仕組みを理解しようとしている場合)にのみ、キーをディレクトリに保持してください。また、alsoそれらのキーを使用するDataProtectionProviderを指定する必要があります。これは、startup.csクラスのUseCookieAuthenticationメソッドのConfigure構成の助けを借りて行うことができます。

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

設定が完了しました。次のコードを使用して、認証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();
    }

この後者のシナリオの詳細については、こちらをご覧ください: https://docs.Microsoft.com/en-us/aspnet/core/security/data-protection/compatibility/cookie-sharing

24
Ron C

Cookieからクレームを取得するには、以下に.NET Core 2のヘルパーメソッドを参照してください。

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

@Ciremが指摘したように、プロテクターを作成する危険な方法は、Microsoftがまさにそれを行う方法です( そのコード を参照)。したがって、将来のバージョンで変更される可能性があります。

9
Alex Klaus

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);
1
Adiqq