How to Encrypt Cookies in ASP.NET Core MVC

To improve the security of your ASP.NET Core MVC application, you must enable cookie encryption. This makes sure that unauthorized users cannot readily read or tamper with sensitive information contained in cookies. Cookie encryption is made easier by built-in mechanisms in ASP.NET Core. We can encrypt the cookies in an ASP.NET Core MVC application using the Data Protection API:

What is Data Protection API?

Protecting sensitive data is the main objective of the Data Protection API, particularly when it has to be momentarily kept in an unsafe place, like form fields or cookies. Safeguarding session states, authentication tokens, and any other sensitive data that needs to be kept between requests are common use cases.

How Can I Use ASP.NET Core MVC’s Data Protection API?

The Data Protection API is used by ASP.NET Core to provide cookie encryption. Generally speaking, the framework takes care of cookie encryption, so you don’t need to. But, you have to manage the encryption yourself if you make custom cookies. The Data Protection API in your program needs to be configured.the following cs file:

builder.Services.AddDataProtection();

How can custom cookies in ASP.NET Core MVC be encrypted?

You must use the Data Protection API manually if you are creating custom cookies and want to make sure they are encrypted. So, copy and paste the following code into a class file called MyCookieService.cs:

using Microsoft.AspNetCore.DataProtection;
namespace SampleMVCWeb.Models
{
    public class MyCookieService
    {
        private readonly IDataProtector _protector;
        public MyCookieService(IDataProtectionProvider dataProtectionProvider)
        {
            //MyCookieProtector must be a unique string value for each Data Protection Provider
            _protector = dataProtectionProvider.CreateProtector("MyCookieProtector");
        }
        //This method will convert the Plain Data to Encrypted value
        public string Protect(string cookieValue)
        {
            return _protector.Protect(cookieValue);
        }
        //This method will convert the Encrypted Data to Plain value
        public string Unprotect(string protectedCookieValue)
        {
            return _protector.Unprotect(protectedCookieValue);
        }
    }
}

The Data Protection API in ASP.NET Core offers a method for encrypting and decrypting data, including cookies. This process depends on the CreateProtector, Protect, and Unprotect methods.

  • CreateProtector Method: Data is encrypted and decrypted using an instance of IDataProtector created by the CreateProtector method. In order to isolate protectors from one another, when you call CreateProtector, you must supply a purpose string.
  • Protect Method: Data encryption is done using the Protect method. Data encryption is done using the Protect method. This method returns encrypted data; to use it, you must pass the plaintext data.
  • Unprotect Method: Encrypted data is decrypted using the Protect method and then the Unprotect method. If the data cannot be decrypted (for example, because it has been altered or the protector was designed for a different function), it will raise an exception.

Storing Cookies

Use the encrypted value when storing cookies in the response header. Use DataProtector to encrypt the value first, and then save it in the Cookies. The following is the syntax to encrypt the cookie:

//Encrypt the Value using Data Protection API
string encryptedValue = myCookieService.Protect("Cookie Value");
//Store the Encrypted Cookie Value in the Response Header
Response.Cookies.Append("myCookie", encryptedValue, cookieOptions);

Reading Cookies

Decrypt the value as you read the cookie. Use the DataProtector to decrypt the value after reading the encrypted value from the cookie. The following is the syntax to decrypt the cookie:

//Fetch the Encrypted Cookie Value from the Response Header
string encryptedValue = Request.Cookies["myCookie"];
//Decrypt the Encryoted Value using the Data Protection API
string decryptedValue = myCookieService.Unprotect(encryptedValue);

Configuring the Service:

The built-in dependency injection container’s MyCookieService needs to be configured next. In the dependency injection (DI) system of ASP.NET Core, registering a service without an interface is simple. Thus, kindly include the following line in the class file program.cs. Here, the AddSingleton method needs to receive the concrete class name.

builder.Services.AddSingleton<MyCookieService>();

Modifying Home Controller:

Then make these modifications to the Home Controller. We use the constructor to first inject the MyCookieService instance, and then we use this instance to encrypt and decrypt the cookies. Additionally, the retrieved cookie values have been saved in the ViewBag, which will be shown in the user interface. This is due to the fact that we do not want to write the logic necessary to retrieve the encrypted data from the cookie and use data that is protected in the View file to decrypt the value.

using Microsoft.AspNetCore.Mvc;
using SampleMVCWeb.Models;
namespace SampleMVCWeb.Controllers
{
    public class HomeController : Controller
    {
        const string CookieUserId = "UserId";
        const string CookieUserName = "UserName";
        MyCookieService _myCookieService;
        public HomeController(MyCookieService myCookieService)
        {
            _myCookieService = myCookieService;
        }
        public IActionResult Index()
        {
            //Let us assume the User is logged in and we need to store the user information in the cookie
            var cookieOptions = new CookieOptions
            {
                HttpOnly = true,
                Secure = true,
                SameSite = SameSiteMode.Strict,
                Expires = DateTime.Now.AddDays(7)
            };
            //Encrypt the Cookie Value
            string encryptedUserId = _myCookieService.Protect("1234567");
            //Store the Encrypted value in the Cookies Response Header
            Response.Cookies.Append(CookieUserId, encryptedUserId, cookieOptions);
            //Encrypt the Cookie Value
            string encryptedUserName = _myCookieService.Protect("[email protected]");
            //Store the Encrypted value in the Cookies Response Header
            Response.Cookies.Append(CookieUserName, encryptedUserName, cookieOptions);
            //If you are coming to this page later,
            //it will also fetch the data from the Cookies Response Header
            string? encryptedUserNameValue = Request.Cookies[CookieUserName];
            if (encryptedUserNameValue != null)
            {
                ViewBag.UserName = _myCookieService.Unprotect(encryptedUserNameValue);
            };
            string? encryptedUserIdValue = Request.Cookies[CookieUserId];
            if (encryptedUserIdValue != null)
            {
                ViewBag.UserId = Convert.ToInt32(_myCookieService.Unprotect(encryptedUserIdValue));
            }
            return View();
        }
        public string About()
        {
            try
            {
                string? UserName = null;
                //Fetch the Encrypted Cookie from the Request Header
                string? encryptedUserNameValue = Request.Cookies[CookieUserName];
                if (encryptedUserNameValue != null)
                {
                    //Decrypt the Encrypted Value
                    UserName = _myCookieService.Unprotect(encryptedUserNameValue);
                };
                int? UserId = null;
                //Fetch the Encrypted Cookie from the Request Header
                string? encryptedUserIdValue = Request.Cookies[CookieUserId];
                if (encryptedUserIdValue != null)
                {
                    //Decrypt the Encrypted Value
                    UserId = Convert.ToInt32(_myCookieService.Unprotect(encryptedUserIdValue));
                }

                string Message = $"UserName: {UserName}, UserId: {UserId}";
                return Message;
            }
            catch (Exception ex)
            {
                return $"Error Occurred: {ex.Message}";
            }
        }
        public IActionResult Privacy()
        {
            //Fetch the Encrypted Cookie from the Request Header
            string? encryptedUserNameValue = Request.Cookies[CookieUserName];
            if (encryptedUserNameValue != null)
            {
                //Store the Decrypted Value in the ViewBag which we will display in the UI
                ViewBag.UserName = _myCookieService.Unprotect(encryptedUserNameValue);
            };
            //Fetch the Encrypted Cookie from the Request Header
            string? encryptedUserIdValue = Request.Cookies[CookieUserId];
            if (encryptedUserIdValue != null)
            {
                //Store the Decrypted Value in the ViewBag which we will display in the UI
                ViewBag.UserId = Convert.ToInt32(_myCookieService.Unprotect(encryptedUserIdValue));
            }
            return View();
        }
        public string DeleteCookie()
        {
            // Delete the Cookie From the Response Header, i.e., from the Browser.
            Response.Cookies.Delete(CookieUserId);
            Response.Cookies.Delete(CookieUserName);
            return "Cookies are Deleted";
        }
    }
}

CookieOptions Class in ASP.NET Core MVC

  • HttpOnly: If the cookie should only be accessible by the server, it can be specified using the HttpOnly property. Client-side scripts cannot access the cookie when it is set to true. By preventing client-side script access to the cookie, this crucial security feature aids in the prevention of cross-site scripting (XSS) attacks. Default Value: false
  • Secure: The Secure property specifies that only HTTPS should be used to send the cookie. This security feature aids in preventing man-in-the-middle attacks, which are attacks where an attacker intercepts the cookie. Default Value: false
  • SameSite: The behavior of sending cross-site and same-site cookies is managed by the SameSite property. It aids in deterring attacks known as cross-site request forgery (CSRF). Default Value: SameSiteMode.Lax (Note: This can vary based on the version of ASP.NET Core and browser behavior.)
  • Expires: The cookie’s expiration date and time are set by the Expires property. The cookie will no longer be sent to the server after this point. In order to manage your cookie’s lifecycle, this property is crucial. The null default value. Cookies are session cookies by default, meaning that their duration is limited to the duration of the browser session.

Modifying Index.cshtml View

@{
     ViewData["Title"] = "Home Page";
}
<div class="text-left">
     <b>User Name:</b> @ViewBag.UserName
     <br />
     <b>User Id:</b> @ViewBag.UserId
</div>

Modifying Privacy.cshtml View

@{
    ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<div class="text-left">
    <b>User Name:</b> @ViewBag.UserName
    <br />
    <b>User Id:</b> @ViewBag.UserId
</div>

After making the aforementioned adjustments, launch the application to see that the cookies are now sent over the network in an encrypted format, as seen in the picture below:

Configuring Cookie Options Globally:

Move such settings to the Program.cs class file if you wish to store cookies using multiple action methods and for those action methods to adhere to the same set of cookie option settings. Thus, kindly add the following code to the class file Program.cs:

builder.Services.ConfigureApplicationCookie(options =>
{
    // Cookie settings
    options.Cookie.HttpOnly = true;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.IsEssential = true; // Make the cookie essential
    options.Cookie.Expiration = TimeSpan.FromDays(1);
});

To set the cookie, you must now use the alternate Append method that does not require the cookie option object. Thus, change the Home Controller class’s Index action method as follows:

public IActionResult Index()
{
    //Let us assume the User is logged in and we need to store the user information in the cookie
    //var cookieOptions = new CookieOptions
    //{ 
    // HttpOnly = true,
    // Secure = true,
    // SameSite = SameSiteMode.Strict,
    // Expires = DateTime.Now.AddDays(7)
    //};
    //Encrypt the Cookie Value
    string encryptedUserId = _myCookieService.Protect("1234567");
    //Store the Encrypted value in the Cookies Response Header
    //Response.Cookies.Append(CookieUserId, encryptedUserId, cookieOptions);
    Response.Cookies.Append(CookieUserId, encryptedUserId);
    //Encrypt the Cookie Value
    string encryptedUserName = _myCookieService.Protect("[email protected]");
    //Store the Encrypted value in the Cookies Response Header
    //Response.Cookies.Append(CookieUserName, encryptedUserName, cookieOptions);
    Response.Cookies.Append(CookieUserName, encryptedUserName);
    //If you are coming to this page later,
    //it will also fetch the data from the Cookies Response Header
    string? encryptedUserNameValue = Request.Cookies[CookieUserName];
    if (encryptedUserNameValue != null)
    {
        ViewBag.UserName = _myCookieService.Unprotect(encryptedUserNameValue);
    };
    string? encryptedUserIdValue = Request.Cookies[CookieUserId];
    if (encryptedUserIdValue != null)
    {
        ViewBag.UserId = Convert.ToInt32(_myCookieService.Unprotect(encryptedUserIdValue));
    }
    return View();
}

After making the aforementioned adjustments, launch the program to ensure it functions as intended.

Which situations in ASP.NET Core MVC call for the use of encrypted cookies?

When you need to store sensitive data in cookies but still keep it safe and secure from manipulation or unwanted access, ASP.NET Core MVC’s encrypted cookies come in handy. In the following specific cases, using encrypted cookies may be beneficial:

  • Storing Sensitive Information:Encrypting the cookies adds an extra layer of security if you need to store sensitive information that shouldn’t be exposed or altered by the client, like preferences or personal identifiers.
  • Preventing Tampering: Cookies that are encrypted make it harder for someone to alter the data that is stored inside of them. Any changes made by the client would render the encrypted content of the cookie invalid.
  • Maintaining Integrity and Confidentiality: Encrypted cookies are the best option when data integrity and confidentiality are critical. They guarantee that data cannot be read or altered by unauthorized parties.
  • Regulatory Compliance: Certain laws, like the GDPR or HIPAA, may mandate that any sensitive or personal information sent online be encrypted. Encrypted cookies make it easier to comply with these regulations.
  • Cross-Site Request Forgery (CSRF) Protection: Using encrypted cookies can help create a more secure application by preventing tampering with the vital information used in CSRF tokens, even though it isn’t a direct solution for CSRF protection.
  • Enhancing Application Security: A more comprehensive strategy for safeguarding web applications includes encrypted cookies. Together with other security measures like HTTPS, secure cookie attributes, and appropriate session management, they should be employed.

Notes:

  • When using secure cookies, make sure your application is operating over HTTPS at all times.
  • Make sure your data protection and cookie policies are up to date with the most recent security standards by reviewing them on a regular basis.
  • Take note of the cookie size limit, which is typically 4KB. Data that has been encrypted will grow in size.

Conclusion

I hope you enjoy reading the article. We will back with other Asp.net core tutorial.

 

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *