Under utvecklingen av min senaste kurs IdentityServer i produktion, upptäckte jag en brist på stöd för att lagra dataskyddsnycklar i Azure Key Vault. I den här bloggposten tänker jag därför förklara hur jag implementerade ett eget stöd.
Vad är syftet med dataskydds-API?
The ASP.NET Core data protection API ligger i hjärtat av ASP.NET Cores säkerhetsstack:
Dataskydds-API:et ansvarar för bland annat:
Kryptering och skydd av sessionscookien som till exempel innehåller information om den inloggade användaren.
Skydd av antiförfalskningscookies som används för CSRF protection
Bilden nedan illustrerar dess roll i ASP.NET Core:
Nycklarna och nyckelringen
För att utföra sitt jobb använder dataskydds-API:et nycklar, och dessa nycklar – både aktuella och utgångna – lagras i en nyckelring.
Nyckelringen kan lagras som den är på flera olika ställen, inklusive i:
Filsystem
Azure blob storage
Redis
Register
Entity Framework Core
För att ytterligare förstärka säkerheten, bör även de råa nycklarna i nyckelringen skyddas när de är vilande. Det här skyddet får man vanligtvis genom att använda någon av följande metoder:
Krypteringsnyckel lagrad i Azure Key Vault
Windows DPAPI (Data Protection Application Programming Interface)
X.509-certifikat
Hur ser en nyckel ut i nyckelringen?
En nyckel representeras av ett XML-dokument, och en exempelnyckel som skyddas med en separat krypterad nyckel kan se ut på följande sätt:
<key id="f3fc6158-523e-40c5-8bcf-28e7f115b36c" version="1">
<creationdate>2020-09-09T17:13:05.8932273Z</creationdate>
<activationdate>2020-09-09T17:13:04.3484725Z</activationdate>
<expirationdate>2020-12-08T17:13:04.3484725Z</expirationdate>
<descriptor deserializertype="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=3.1.8.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
<descriptor>
<encryption algorithm="AES_256_CBC"></encryption>
<validation algorithm="HMACSHA256"></validation>
<encryptedsecret decryptortype="Microsoft.AspNetCore.DataProtection.AzureKeyVault.AzureKeyVaultXmlDecryptor
,Microsoft.AspNetCore.DataProtection.AzureKeyVault, Version=3.1.8.0, Culture=neutral,
PublicKeyToken=adb9793829ddae60" xmlns="http://schemas.asp.net/2015/03/dataProtection">
<encryptedkey xmlns="">
<!-- This key is encrypted with Azure KeyVault. →
<kid>https://XXXXXXXX.vault.azure.net/keys/dataprotectionkey/864cf386ccf....</kid>
<key>EWRE6Pe+Bba16k023sJ7SbTJ.......</key>
<iv>7KpTr5oDJAJlQqAyjESL5Q==</iv>
<value>ISoWcWY+R19b+LKVI6YLWEt1fctp....</value>
</encryptedKey>
</encryptedSecret>
</descriptor>
</descriptor>
</key>
(Nyckeln och värdet är förkortade för tydlighetens skull)
Vad händer om jag inte konfigurerar dataskyddstjänsten i ASP.NET Core?
Din webbplats kommer att fungera, användare kan logga in och allting skyddas automatiskt. Som standard skapas en lokal nyckelring vid uppstart och lagras i ditt lokala filsystem. Problemet kommer däremot när du driftsätter din applikation på nytt. Om du till exempel driftsätter med hjälp av containrar, förloras den lokalt skapade nyckelringen och en ny skapas på dess plats när containern driftsätts.
När dina användare därefter fortsätter att besöka din webbplats, är alla existerande sessionscookies ogiltiga. Det beror på att den nya krypteringsnyckeln inte längre är den samma som den som användes för att skapa existerande cookies. Resultatet blir att alla inloggade användare loggas ut och måste logga in på nytt, vilket är väldigt frustererande för användarna!
Även existerande antiförfalskningstokens kommer att bli ogiltiga, vilket kan leda till att användares formulär misslyckas.
Var ska nyckelringen lagras?
Vi måste alltså placera nyckelringen på en permanent plats utanför applikationen där den inte påverkas av upprepade driftsättningar. Så var kan man då lagra den?
När jag designar system, föredrar jag att fokusera på att reducera komplexiteten, och att lagra nyckelringen och krypteringsnyckeln på olika ställen kändes lite överdrivet. Så varför inte lagra även nyckelringen i Azure Key Vault?
Problemet är att det inte finns någon lösning för att lagra nyckelringar i Azure Key Vault i dagsläget (som jag kunde hitta), så jag beslöt mig för att skapa en egen. Den huvudsakliga anledningen till att jag skapade den, var för att jag ville använda den i min kurs IdentityServer i Produktion, samt lära mig mer om Azure Key Vault.
Målet är att lagra både nyckelringen och krypteringsnyckeln i samma Vault, som bilden nedan visar:
Lagring av nyckelringar i Azure Key Vault
Nyckelringen består faktiskt av ett vanligt XML-dokument, så allt vi behöver göra är att lagra den som en hemlighet i Azure Key Vault. För att undvika kodningsproblem, gör vi först en base64-kodning av nyckelringen innan vi skickar den till AKV. Du kan prova att hoppa över det här steget, men det har jag inte testat.
Begränsningar! Åh, nej!
Azure Key Vault har dock en begränsning; den maximala storleken på ett hemligt värde är 25Kb. Det innebär att vi i praktiken endast kan lagra 10-11 nycklar i vår nyckelring innan vi når gränsen. Dataskydds-API:et roterar visserligen endast nycklar var 90:e dag som standard, så om du bara har ett behov att skydda sessions- och antiförfalskningstokens, behöver du endast lagra några av de senaste nycklarna.
OBS! Om du tänker kryptera data som lagras under lång tid (t.ex. känsliga eller kunddata i din databas), måste du lagra nyckelringen någon annanstans, då du måste spara alla gamla nycklar för att kunna avkryptera dina data.
Användning
För att använda min implementation behöver du endast lägga till följande i din Startup.cs-konfigurationsmetod:
services.AddDataProtection()
.PersistKeysToAzureKeyVault(...[parameters]...)
.ProtectKeysWithAzureKeyVault(...[parameters]...);
PersistKeysToAzureKeyVault används för att lägga till och konfigurera lagringsleverantören. ProtectKeysWithAzureKeyVault används för att kryptera nycklar i vila med hjälp av en krypteringsnyckel som lagras i Azure Key Vault.
Källkod
Du hittar hela källkoden plus en demoapplikation i AzureKeyVaultKeyRingRepository.
Feedback önskas!
Kommentarer? Synpunkter? Förslag? Kontakta mig gärna, hör av dig via LinkedIn eller följ mig på Twitter.
IdentityServer i produktion
Det här blogginlägget utvecklades som en del av min research inför min nya kurs och om du är nyfiken på den här kursen kan du läsa mer om det här.
Comentários