ASP.NET Core issues several cookies, including authentication, antiforgery, and session cookies. This blog post will explore what these cookies contain and how they are protected.

Exploring what is inside the ASP.NET Core cookies

ASP.NET Core issues several cookies, including authentication, antiforgery, and session cookies. This blog post will explore what these cookies contain and how they are protected.

Protecting the cookies

The content of these cookies is protected through a combination of encryption and signing mechanisms. These protective measures ensure the confidentiality and integrity of the information stored within the cookies.

The protection is managed by the Data Protection API  (DPAPI) as shown below:

The Data Protection API in ASP.NET Core is a framework for securing sensitive data, managing encryption, and facilitating key rotation. It offers a unified interface for encryption, decryption, and signing that enhances application data security. How the Data Protection API works is beyond the scope of this blog post.

The Authentication Cookie

The Authentication Cookie in ASP.NET Core maintains user authentication across HTTP requests. When a user logs in, the authentication system typically issues a cookie to the client, appearing as follows:

				
					Set-Cookie: .AspNetCore.cookie=CfDJ8MSO_2XEvwalH...;
expires=Thu, 30 Nov 2023 09:38:16 GMT; path=/; secure; samesite=lax; httponly
				
			

The Data Protection API encrypts this cookie, making its contents inaccessible and tamperproof.

So, how can we peek inside this cookie?

Luckily for us, we can turn off this protection by providing a custom transparent data protector, as shown below:

				
					public class MyDataProtector : IDataProtector
{
    public IDataProtector CreateProtector(string purpose)
    {
        return new MyDataProtector();
    }

    public byte[] Protect(byte[] plaintext)
    {
        return plaintext;
    }

    public byte[] Unprotect(byte[] protectedData)
    {
        return protectedData;
    }
}

				
			
We can then add this protector to the authentication cookie handler:
				
					builder.Services.AddAuthentication("cookie")
.AddCookie("cookie", o =>
{
    o.DataProtectionProvider = new MyDataProtector();
});


				
			
This means that the issued cookies will not be protected at all. To test this and issue a new unsecured cookie, we first need to sign in a user. Wecan do this by using this simple test code:
				
					app.MapGet("/login", async context =>
{
    var claims = new Claim[]
    {
        //Standard claims
        new Claim(ClaimTypes.Name, "Tore Nestenius"),
        new Claim(ClaimTypes.Country, "Sweden"),
        new Claim(ClaimTypes.Email, "tore@tn-data.se"),

        //Custom claims
        new Claim("JobTitle", "Consultant and trainer"),
        new Claim("JobLevel", "Senior"),
        new Claim("webpage", "https://www.tn-data.se"),
    };

    var identity = new ClaimsIdentity(claims: claims,
                                      authenticationType: "cookie");

    var user = new ClaimsPrincipal(identity: identity);

    var authProperties = new AuthenticationProperties
    {
    };

    //Sign-in the user
    await context.SignInAsync("cookie", user, authProperties);

    await context.Response.WriteAsync("<!DOCTYPE html><body>");
    await context.Response.WriteAsync("<h1>Logged in!</h1>");
});

				
			

In the code above, we create a ClaimsPrincipal user object and ask the cookie handler to sign this user. By doing so, the handler will issue a new unprotected authentication cookie.

Running this code will issue a new cookie that can look like this:

				
					Set-Cookie: 
.AspNetCore.cookie=BQAAAAZjb29raWUBAAAABmNvb2tpZQEAAQAGAAAAAQAOVG9yZSBOZXN0ZW5pdXMBAAEA
AQAAAAAAPWh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2NvdW50
cnkGU3dlZGVuAQABAAEAAAAAAEJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2
NsYWltcy9lbWFpbGFkZHJlc3MPdG9yZUB0bi1kYXRhLnNlAQABAAEAAAAAAAhKb2JUaXRsZRZDb25zdWx0YW50IGFuZ
CB0cmFpbmVyAQABAAEAAAAAAAhKb2JMZXZlbAZTZW5pb3IBAAEAAQAAAAAAB3dlYnBhZ2UWaHR0cHM6Ly93d3c
udG4tZGF0YS5zZQEAAQABAAAAAAAAAAEAAAADAAAACy5wZXJzaXN0ZW50AAcuaXNzdWVkHVRodSwgMTYgTm92
IDIwMjMgMDk6NTc6MDQgR01UCC5leHBpcmVzHVRodSwgMzAgTm92IDIwMjMgMDk6NTc6MDQgR01U;
expires=Thu, 30 Nov 2023 09:57:04 GMT; path=/; secure; samesite=lax; httponly


				
			

To peek inside the cookie, we need to first base64 decode it. We can do that using a tool like Base64Decode, and if we do that, we will see the following:

				
					......cookie.....cookie...........Tore Nestenius..........
=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country.Sweden..........B
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress.tore@tn-data.se
...........JobTitle.Consultant and trainer...........JobLevel.Senior
...........webpage.https://www.tn-data.se......................persistent...issued.
Thu, 16 Nov 2023 09:57:04 GMT..expires.Thu, 30 Nov 2023 09:57:04 GMT



				
			

(The non-printable characters have been replaced with a dot).

This shows that inside the cookie we find the entire authentication ticket (consisting of the ClaimsPrincipal and the AuthenticationProperties).

The session cookie

The session service in ASP.NET Core is a mechanism for managing user-specific data across  requests, often being used for scenarios like maintaining a shopping cart. This service issues a protected cookie to the browser to keep track of the data.  It is important to know that this cookie and its purpose are unrelated to the earlier authentication cookie.

Your application can ask the session service to remember temporary data for the current user, as shown below:

To implement the session service, register the service and add it to the request pipeline:

				
					// Register the service
builder.Services.AddSession(options =>
{
    //...Options…
});

//..
app.UseSession();

				
			
A sample session cookie could look like:
				
					Set-Cookie: 
.AspNetCore.Session=CfDJ8MSO%2F2XEvalHlF%2Fgv69RLqD6mFWeTVC1MG3dYxNWftq625VyGyqF%2BeIq2
xaqgm1cd4McTp0ydSLcRYraIA4%2F%2Bn89FKFhz567AcC%2FeSnwabg4eRrlFAeWLFBq0K8zl2ISdMPcY0pj%
2BtAJQgC5NIte76QR4TlheM1ZhsD98WAdAvKM; path=/; samesite=lax; httponly
				
			

This cookie is also protected using the Data Protection API; however, there is no way to provide a custom transparent data protector, as we did in the previous example.

So, how can we peek inside this cookie?

One option is to download and modify the code for the session middleware. The source code is found on GitHub, and it is straightforward to modify. When I introduce the transparent protector, the unencrypted cookie will look like this:

				
					Set-Cookie: .AspNetCore.Session=Y2M1NGVjZGItZDhjNi0yYTI1LTM2N2UtMjhhNmM4ZWRhMzMw;
path=/; samesite=lax; httponly
				
			
This cookie is also base64 encoded, so decoding the cookie will result in the following data:
				
					cc54ecdb-d8c6-2a25-367e-28a6c8eda330
				
			

This GUID acts as a session key used to look up the session object for the current request. Where the data is stored depends on how you have configured the session system.

The session system in ASP.NET Core provides various storage options, including in-memory storage, distributed cache, and external storage providers, which allowsdevelopers to choose the most suitable method for their application’s scalability and performance needs.

The Antiforgery Cookie

This cookie is crucial forsafeguarding against Cross-site Request Forgery (CSRF) attacks. This cookie operates on a token-based system, where the token is stored within the cookie and also embedded in HTML forms. When a form is submitted, the token in the form data must correspond with the token in the cookie to ensure security.

Additionally, the Data Protection API secures this cookie. The implementation of this protection is handled by the DefaultAntiforgeryTokenSerializer class, which you can explore in detail here .

Here’s an example of how this cookie can beset in ASP.NET Core:

				
					Set-Cookie: .AspNetCore.Antiforgery.YCD23e8AirM=CfDJ8MSO_2XEvalHlF_gv69RLqDarftldkzWvbxvac
LnZ4WtaTDcgSCPmxQdmK8OHbGfMIUvV0VhcFkx4Ys8jPppklGBUGWRTsXcwxhMBl7nqZA0s_xYN5jJq3J7
LrH8EikY82bOYmSoA_wEYCxkcrBEEAs; path=/; samesite=strict; httponly


				
			

While this blog post does not delve into the specifics of the token’s internal structure, those interested in this can refer to the Default Antiforgery Token Serializer class for an in-depth understanding of how the token is constructed.

Conclusions

The blog delves into ASP.NET Core cookies, specifically the authentication, session, and antiforgery cookies, shedding light on their contents. It emphasizes the security aspects involving encryption and signing through the Data Protection API.

  • The authentication cookie
    This cookie contains the entire ClaimsPrincipal user object and the associated authentication properties.
  • The antiforgery cookie
    The token of this cookie is used to protect HTML forms.
  • The session cookie
    This cookie contains the session key that the session service can use to look up the stored data for a given user.

Remember that you should never disable data protection in real production applications!

Feedback, comments, found any bugs?

Let me know if you have any feedback, anything I missed, or any bugs/typos. You can find my contact details here.

About the author

Hi, I’m Tore! I have been fascinated by computers since I unpacked my first Commodore VIC-20. Today, I enjoy providing freelance development and developer training services, focusing on ASP.NET Core, IdentityServer, OpenID Connect, Architecture, and Web Security. You can connect with me on LinkedIn and Twitter, and you can find out more about me and my services here, as well as my courses for developers, including my course, Introduction to IdentityServer and OpenID-Connect.

Share This Story
Share on linkedin
Share on twitter
Share on facebook
Share on pinterest
Share on email

About me

My name is Tore Nestenius and I’m a trainer and senior software developer focusing on Architecture, Security and Identity, .NET, C#, Backend, and Cloud, among other things.

Do You Want Tore To Be Your Mentor?

Categories