Valet-nyckelmönster

Använd ett token som ger klienter begränsad direktåtkomst till en viss resurs för att avlasta dataöverföring från programmet. Det är särskilt användbart i program där molnbaserade lagringssystem eller köer används, och kan minimera kostnaderna och maximera skalbarhet och prestanda.

Kontext och problem

Klientprogram och webbläsare behöver ofta läsa och skriva filer eller dataströmmar till och från ett programs lagring. Normalt hanterar programmet flödet av data – antingen genom att hämta dem från lagringen och strömma dem till klienten, eller genom att läsa den uppladdade dataströmmen från klienten och lagra den i datalagret. Den här metoden absorberar dock värdefulla resurser som beräkning, minne och bandbredd.

Datalager kan hantera överföring och hämtning av data direkt, utan att programmet behöver utföra bearbetningen för att flytta data. Men för detta krävs vanligtvis att klienten har åtkomst till lagrets säkerhetsautentiseringsuppgifter. Det kan vara en användbar teknik för att minimera kostnaderna för dataöverföring och skaländringskraven för programmet, och för att maximera prestandan. Det innebär dock att programmet inte längre kan hantera datasäkerheten. När klienten har en anslutning till datalagret för direktåtkomst kan inte programmet fungera som gatekeeper. Det styr inte längre processen och kan inte förhindra efterföljande överföringar eller hämtningar från datalagret.

Detta är inte en realistisk metod i distribuerade system som behöver hantera klienter som inte är betrodda. I stället måste programmen kunna styra dataåtkomsten säkert och detaljerat, men ändå minska belastningen på servern genom att konfigurera den här anslutningen och därefter låta klienten kommunicera direkt med datalagret för de nödvändiga läs- och skrivåtgärderna.

Lösning

Du måste lösa problemet med att styra åtkomsten till ett datalager där lagret inte kan hantera autentisering och auktorisering av klienter. En vanlig lösning är att begränsa åtkomsten till datalagrets offentliga anslutning och ge klienten en nyckel eller token som datalagret kan verifiera.

En sådan nyckel eller token brukar benämnas valet-nyckel. Den ger tidsbegränsad åtkomst till specifika resurser och tillåter endast fördefinierade åtgärder som att läsa och skriva till lagring eller köer, eller att ladda upp och ladda ned i en webbläsare. Programmen kan snabbt och enkelt skapa och utfärda valet-nycklar till klientenheter och webbläsare, så att klienterna tillåts att utföra nödvändiga åtgärder utan att programmet behöver hantera dataöverföringen direkt. Detta eliminerar bearbetningskostnaderna och påverkan på prestanda och skalbarhet från programmet och servern.

Klienten använder sin token för att få åtkomst till en specifik resurs i datalagret under en viss tidsperiod och med specifika åtkomstbegränsningar, som bilden visar. Efter den angivna perioden blir nyckeln ogiltig och tillåter inte åtkomst till resursen.

Figur 1 – Översikt över mönstret

Du kan också konfigurera en nyckel som har andra beroenden, till exempel dataomfång. Beroende på datalagerfunktionerna kan nyckeln till exempel specificera en hel tabell i ett datalager eller endast vissa rader i en tabell. I molnlagringssystem kan nyckeln specificera en container eller enbart ett visst objekt i containern.

Programmet kan också göra nyckeln ogiltig. Det är en bra metod om klienten meddelar servern att dataöverföringen har slutförts. Servern kan sedan ogiltigförklara nyckeln för att förhindra ytterligare åtkomst.

Det här mönstret kan förenkla hanteringen av resursåtkomst eftersom det inte finns några krav på att skapa och autentisera en användare, bevilja behörighet och sedan ta bort användaren. Det gör det också enkelt att begränsa platsen, behörigheten och giltighetsperioden – allt genom att helt enkelt generera en nyckel vid körning. Viktiga faktorer är att begränsa giltighetsperioden och i synnerhet platsen för resursen så mycket som möjligt, så att mottagaren kan bara använda den för det avsedda syftet.

Problem och överväganden

Tänk på följande när du bestämmer hur du ska implementera mönstret:

Hantera nyckelns giltighetsstatus och tidsperiod. Om nyckeln läcker ut eller på annat sätt komprometteras, låser den upp målobjektet och gör det tillgängligt för skadlig användning under giltighetsperioden. En nyckel kan vanligtvis återkallas eller inaktiveras, beroende på hur det har utfärdats. Principer på serversidan kan ändras eller den servernyckel som den signerades med kan vara ogiltig. Ange en kort giltighetsperiod för att minimera risken för att obehöriga åtgärder ska utföras i datalagret. Men om giltighetsperioden är för kort kanske inte klienten hinner slutföra åtgärden innan nyckelns giltighet går ut. Tillåt behöriga användare att förnya nyckeln innan giltighetsperioden går ut om fler behöver ha åtkomst till den skyddade resursen.

Styr nyckelns åtkomstnivå. Vanligtvis ska nyckeln endast tillåta användaren att utföra det som krävs för att slutföra åtgärden, till exempel skrivskyddad åtkomst om klienten inte ska kunna ladda upp data till datalagret. För filuppladdning är det vanligt att ange en nyckel som endast ger skrivbehörighet, samt plats och giltighetsperiod. Det är viktigt att den resurs eller resursuppsättning som gäller för nyckeln anges korrekt.

Överväg hur du ska styra användarnas beteende. Om du implementerar det här mönstret innebär det en viss förlust av kontroll över de resurser som användarna beviljas åtkomst till. Vilken kontrollnivå som kan användas begränsas av funktionerna i de principer och behörigheter som är tillgängliga för tjänsten eller i måldatalagret. Till exempel går det vanligtvis inte att skapa en nyckel som begränsar storleken på data som ska skrivas till lagringen eller antalet gånger som nyckeln kan användas för åtkomst till en fil. Detta kan ge mycket höga, oväntade kostnader för dataöverföring, även när den används av den avsedda klienten. Det kan orsakas av ett fel i koden som gör att upp- eller nedladdning görs upprepade gånger. Om du vill begränsa antalet gånger som en fil kan laddas upp kan du där det är möjligt tvinga klienten att meddela programmet när en åtgärd har slutförts. Vissa datalager skapar till exempel händelser som programkoden kan använda för att övervaka åtgärder och styra användarbeteendet. Det kan dock vara svårt att genomdriva kvoter för enskilda användare i ett scenario med flera klientorganisationer där samma nyckel används av alla användare från en klientorganisation.

Validera alla överförda data och eventuellt rensa dem. En obehörig användare som får åtkomst till nyckeln kan ladda upp data som är avsedd att skada systemet. Auktoriserade användare kan också ladda upp data som är ogiltiga. När dessa behandlas kan det orsaka ett fel eller systemfel. För att skydda dig mot det kan du säkerställa att alla uppladdade data verifieras och söks igenom efter skadlig kod innan de används.

Granska alla åtgärder. Många nyckelbaserade mekanismer kan logga åtgärder som till exempel uppladdningar, nedladdningar och fel. De här loggarna kan ingå i en granskningsprocess och kan även användas för fakturering om användaren debiteras enligt filstorlek eller datavolym. Använd loggarna för att identifiera autentiseringsfel kan bero på problem med nyckelleverantören eller oavsiktlig borttagning av en lagrad åtkomstprincip.

Leverera nyckeln på ett säkert sätt. Den kan vara inbäddad i en URL som användaren aktiverar på en webbsida eller användas i en serveromdirigering så att nedladdningen sker automatiskt. Använd alltid HTTPS för att leverera nyckeln via en säker kanal.

Skydda känsliga data under överföringen. För känsliga data som levereras via programmet används vanligtvis SSL eller TLS, och detta ska tillämpas för klienter som har direktåtkomst till datalagret.

Här är några andra problem som du bör vara medveten om när du implementerar det här mönstret:

  • Om klienten inte meddelar eller inte kan meddela servern när åtgärden har slutförts, och den ena begränsningen är nyckelns giltighetsperiod, kan inte programmet utföra granskningsåtgärder som att räkna antalet upp- eller nedladdningar eller förhindra flera upp- eller nedladdningar.

  • Flexibiliteten när det gäller vilka nyckelprinciper som kan genereras kan begränsas. Till exempel kanske en del mekanismer endast tillåter användning av en tidsinställd giltighetsperiod. Andra inte kan ange en tillräcklig detaljnivå för läs- och skrivbehörighet.

  • Om du anger starttid för giltighetsperioden för din nyckel eller token måste du se till att den är lite tidigare än aktuell servertiden, ifall klientens klockor inte är exakt synkroniserade. Om ingen standard har angetts är standard den aktuella servertiden.

  • Den URL som innehåller nyckeln kommer att loggas i serverloggfilerna. Nyckeln hinner vanligtvis gå ut innan loggfilerna analyseras, men se ändå till att begränsa åtkomsten till loggfilerna. Om loggdata skickas till ett övervakningssystem eller lagras på en annan plats bör du överväga att använda en fördröjning för att förhindra att nycklar läcker ut innan giltighetstiden har gått ut.

  • Om klientkoden körs i en webbläsare kan webbläsaren behöva ha stöd för resursdelning över flera ursprung (CORS) om du vill aktivera kod som körs i webbläsaren för åtkomst till data i en annan domän än den som användes för sidan. Vissa äldre webbläsare och vissa datalager stöder inte CORS, och kod som körs i dessa webbläsare kanske inte kan använda en valet-nyckel för att ge åtkomst till data i en annan domän, till exempel ett molnlagringskonto.

När du ska använda det här mönstret

Det här mönstret är användbart i följande situationer:

  • Minimera resursinläsningen och maximera prestanda och skalbarhet. När en valet-nyckel används måste inte resursen vara låst, inget fjärrserveranrop krävs, det finns ingen gräns för hur många valet-nycklar som kan utfärdas och det förhindrar att en enskild felpunkt uppstår på grund av dataöverföring via programkoden. Att skapa en valet-nyckel är oftast en enkel krypteringsåtgärd där en sträng signeras med en nyckel.

  • Minimera driftskostnaderna. Aktivera direktåtkomst till butiker och köer är resurs- och kostnadseffektivt, kan ge färre nätverksförfrågningar och kan göra att ett mindre antal beräkningsresurser krävs.

  • När klienter regelbundet laddar upp eller ned data, i synnerhet om det är en stor volym eller om varje åtgärd omfattar stora filer.

  • När programmet har begränsade beräkningsresurser tillgängliga, antingen på grund av värdbegränsningar eller kostnadsöverväganden. I det här scenariot är mönstret ännu mer användbart om det finns många samtidiga upp- eller nedladdningar eftersom programmet inte behöver hantera dataöverföringen.

  • När data lagras i ett fjärranslutet datalager eller ett annat datacenter. Om programmet måste fungera som en gatekeeper kan det tillkomma kostnader för ytterligare bandbredd för överföring av data mellan datacenter eller i offentliga och privata nätverk mellan klienten och programmet och därefter mellan programmet och datalagret.

Det här mönstret kanske inte är användbart i följande situationer:

  • Om programmet måste utföra någon uppgift på informationen innan den lagras eller innan den skickas till klienten. Om programmet till exempel behöver utföra verifiering, logga åtkomst eller köra en dataomvandling. Vissa datalager och klienter kan dock förhandla och utföra enkla omvandlingar, till exempel komprimering och dekomprimering (till exempel kan en webbläsare vanligtvis hantera gzip-format).

  • Om utformningen av ett befintligt program gör det svårt att införliva mönstret. När man använder det här mönstret krävs oftast en annan arkitekturmetod för att leverera och ta emot data.

  • Om det är nödvändigt att underhålla granskningshistorik och styra hur många gånger en överföringsåtgärd körs, och den valet-nyckelmekanism som används inte har stöd för aviseringar som servern kan använda för att hantera åtgärderna.

  • Om det är nödvändigt att begränsa datastorleken, särskilt under uppladdningar. Den enda lösningen på detta är att programmet kontrollerar datastorleken när åtgärden har slutförts eller kontrollerar storleken på uppladdningar efter en angiven tidsperiod eller enligt ett schema.

Exempel

Azure har stöd för signaturer för delad åtkomst i Azure Storage för detaljerad åtkomstkontroll till blobbar, tabeller och köer samt för Service Bus-köer och -ämnen. En signaturtoken för delad åtkomst kan konfigureras för att tillhandahålla särskilda behörigheter som läs-, skriv- uppdaterings- och raderingsbehörighet till en viss tabell, ett nyckelintervall inom en tabell, en kö, en blob eller en blob-container. Giltigheten kan vara en angiven tidsperiod eller inte ha någon tidsgräns.

Signaturer för delad åtkomst i Azure har också stöd för serverlagrade åtkomstprinciper som kan kopplas till en specifik resurs, till exempel en tabell eller blob. Den här funktionen ger ytterligare kontroll och flexibilitet jämfört med programgenererade signaturtoken för delad åtkomst och bör användas när det är möjligt. Inställningar som definierats i en serverlagrad princip kan ändras, och de visas i token utan en ny token behöver utfärdas, men inställningarna som definieras i token kan inte ändras utan att en ny token utfärdas. Den här metoden gör det också möjligt att återkalla en giltig delad signaturtoken för delad åtkomst innan giltighetstiden har gått ut.

Mer information finns i Bevilja begränsad åtkomst till Azure Storage resurser med hjälp av signaturer för delad åtkomst (SAS).

Följande kod visar hur du skapar en signaturtoken för delad åtkomst som är giltig i fem minuter. Metoden GetSharedAccessReferenceForUpload returnerar en signaturtoken för delad åtkomst som kan användas för att lada upp en fil till Azure Blob Storage.

public class ValuesController : ApiController
{
  private readonly BlobServiceClient blobServiceClient;
  private readonly string blobContainer;
  ...
  /// <summary>
  /// Return a limited access key that allows the caller to upload a file
  /// to this specific destination for a defined period of time.
  /// </summary>
  private StorageEntitySas GetSharedAccessReferenceForUpload(string blobName)
  {          
      var blob = blobServiceClient.GetBlobContainerClient(this.blobContainer).GetBlobClient(blobName);
      var storageSharedKeyCredential = new StorageSharedKeyCredential(blobServiceClient.AccountName, ConfigurationManager.AppSettings["AzureStorageEmulatorAccountKey"]);

      var blobSasBuilder = new BlobSasBuilder
      {
          BlobContainerName = this.blobContainer,
          BlobName = blobName,
          Resource = "b",
          StartsOn = DateTimeOffset.UtcNow.AddMinutes(-5),
          ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(5)
      };
      blobSasBuilder.SetPermissions(BlobSasPermissions.Write);

      return new StorageEntitySas
      {
          BlobUri = blob.Uri,
          Credentials = blobSasBuilder.ToSasQueryParameters(storageSharedKeyCredential).ToString()
      };
  }
  public struct StorageEntitySas
  {
      public string Credentials;
      public Uri BlobUri;
  }
}

Hela exemplet finns tillgängligt i ValetKey-lösningen som kan laddas ned från GitHub. Projektet ValetKey.Web i den här lösningen innehåller en webbapp som omfattar den ValuesController-klass som visas ovan. Ett exempelklientprogram som använder den här webbappen för att hämta en signaturnyckel för delad åtkomst och ladda upp en fil till blob-lagring finns i projektet ValetKey.Client.

Nästa steg

Följande riktlinjer kan vara relevanta när du implementerar det här mönstret:

Följande mönster kan också vara relevanta när du implementerar det här mönstret:

  • Gatekeeper-mönster. Det här programmet kan användas tillsammans med mönstret Valet-nyckel för att skydda program och tjänster med hjälp av en dedikerad värdinstans som fungerar som koordinator mellan klienter och programmet eller tjänsten. Gatekeepern validerar och sanerar begäranden, och skickar begäranden och data mellan klienten och programmet. Detta kan ge ett extra lager säkerhet och minska risken för angrepp på systemet.
  • Mönster för värddator för statiskt innehåll. Beskriver hur du distribuerar statiska resurser till en molnbaserad lagringstjänst som kan leverera dessa resurser direkt till klienten för att minska behovet av dyra beräkningsinstanser. Valet-nyckelmönstret kan användas för att skydda resurserna där de inte är avsedda att vara allmänt tillgängliga.