[Se]   En

Kryptering av cookies

  Annice      2021-03-19      C#, ASP.NET Core

Cookies är ju vanligt att använda sig av i webbutvecklingssammanhang, t.ex. för att komma ihåg en viss användarinloggning då man kan sätta en cookie på ett inloggat användar-ID. Cookie-värden som sparas i webbläsaren innebär dock att vem som helst kan se dessa värden genom att välja att inspektera webbläsarens element.

Av säkerhetsskäl vill man helst undvika att exponera faktiska användar-ID:n som läses fram från databaser för obehöriga, och ett sätt att dölja användar-ID:n som används i cookies kan då vara att kryptera dessa cookie-värden.

Inom C# ASP.NET Core kan detta göras genom att t.ex. använda sig av tillägget DataProtection. I en MVC ASP.NET Core-lösning görs detta genom att först registrera tillägget ifråga i filen "Startup.cs" enligt nedan:

 public class Startup
 {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();

            // Register support for DataProtection extension:
            services.AddDataProtection();
        }

       // ...

Därefter kan man sedan skapa en separat tjänsteklass som imlementerar detta DataProtection-tillägg, t.ex. genom följande klass och kod som kan läggas i ett eget tjänstelager (mapp) i en applikation:

using System;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;

namespace TestApp.Services
{
    public class DataProtector
    {
        private IDataProtector CookieProtector()
        {
            var dataProtectionProvider = DataProtectionProvider.Create("TestApp");
            var protector = dataProtectionProvider.CreateProtector("Cookie.Encryption");
            return protector;
        }

        /// Create a user cookie and encrypt it to prevent the real value
        /// from being exposed on the client side (via the browser).
        public void EncryptUserId(HttpContext context, int userId)
        {
            string protectedUserId = CookieProtector().Protect(userId.ToString());
            context.Response.Cookies.Append("User", protectedUserId);
        }

        /// Receive the encrypted user cookie and decrypt it to be able to handle its
        /// real value on the server side.
        public int? ReadUserId(HttpContext context)
        {
            int userId;

            if (!string.IsNullOrEmpty(context.Request.Cookies["User"]))
            {
                string id = CookieProtector().Unprotect(context.Request.Cookies["User"]);
                userId = Convert.ToInt32(id);
                return userId;
            }
            return (int?)null;
        }

    }
}

För att sedan nyttja denna tjänst vid rendering (skapande) av webbsidor genom en controller kan det se ut så här om man vill försäkra sig om att cookien är krypterad innan man presenterar en sida vid namn "Start":

using Microsoft.AspNetCore.Mvc;
using TestApp.Services; // Include the service layer to reach our data protector service.

namespace TestApp.Controllers
{
    public class HomeController : Controller
    {
        // Autowire our DataProtector service via dependency injection:
        public IActionResult Start(DataProtector protector)
        {
            // E.g. userId fetched from DB, but hard coded in this example for simplicity:
            int userId = 1;
            protector.EncryptUserId(HttpContext, userId);

            // ...

            return View();
        }
    
     }
 }

Efter att ovanstående användar-ID satts som cookie samt krypterats via ovanstående tjänst kommer cookie-värdet "1" alltså bara exponeras som ett krypterat värde i webbläsaren enligt följande screenshot:

När man sedan vill dekryptera cookie-värdet för att kunna behandla dess faktiska värde på serversidan igen kan detta göras genom att anropa den implementerade tjänsteklassen som dekrypterar värdet likt exemplet nedan:

/// A method used in a suitable place on the server-side where we want to call our DataProtection service
/// via a dependency injection to decrypt our cookie back to its actual value.
public void HandleDecryptedCookieValue(DataProtector protector)
{
    // This will assign a userId variable with the decrypted cookie value:
    var userId = protector.ReadUserId(HttpContext);

    // Now do something with the decrypted userId...
}