Modello di passepartout

Azure
Archiviazione di Azure

Usare un token che fornisca ai client l'accesso diretto limitato a una specifica risorsa per eseguire l'offload del trasferimento dei dati dall'applicazione. Questa condizione risulta particolarmente utile nelle applicazioni che usano code o sistemi di archiviazione ospitati nel cloud e permette di ridurre i costi e ottimizzare scalabilità e prestazioni.

Contesto e problema

I programmi client e i Web browser spesso devono leggere e scrivere file o flussi di dati da e verso l'archiviazione di un'applicazione. In genere, l'applicazione gestirà lo spostamento dei dati, recuperandoli dall'archiviazione e trasmettendoli al client oppure leggendo il flusso caricato dal client e archiviandolo nell'archivio dati. Questo approccio assorbe tuttavia risorse utili, ad esempio risorse di calcolo, memoria e larghezza di banda.

Gli archivi dati sono in grado di gestire direttamente l'upload e il download dei dati, senza che l'applicazione debba eseguire un'elaborazione per spostare questi dati. In questi casi, tuttavia, il cliente potrebbe dover accedere alle credenziali di sicurezza per l'archivio. Può trattarsi di una tecnica utile per ridurre al minimo i costi di trasferimento dei dati e l'esigenza di aumentare il numero di istanze per l'applicazione, nonché per ottimizzare le prestazioni. L'applicazione, tuttavia, non è più in grado di gestire la sicurezza dei dati. Dopo che il client avrà stabilito una connessione all'archivio dati per l'accesso diretto, l'applicazione non potrà fungere da gatekeeper. Non è più in controllo del processo e non può impedire upload o download successivi dall'archivio dati.

Ciò non costituisce un approccio realistico nei sistemi distribuiti che gestiscono client non attendibili. Al contrario, le applicazioni devono essere in grado di controllare in modo sicuro l'accesso ai dati in modo granulare, ma comunque ridurre il carico sul server configurando questa connessione e poi consentendo al client di comunicare direttamente con l'archivio dati per eseguire le operazioni di lettura o scrittura necessarie.

Soluzione

È necessario risolvere il problema di controllo dell'accesso a un archivio dati in cui l'archivio non possa gestire l'autenticazione e l'autorizzazione dei client. Una soluzione tipica consiste nel limitare l'accesso alla connessione pubblica dell'archivio dati e fornire al client una chiave o un token che l'archivio dati può convalidare.

Questa chiave o token è in genere definita passepartout. Fornisce accesso limitato al tempo a risorse specifiche e consente solo operazioni predefinite con un controllo granulare, ad esempio la scrittura nell'archiviazione, ma non la lettura, il caricamento e il download in un Web browser. Le applicazioni possono creare ed emettere passepartout per dispositivi client e Web browser in modo rapido e semplice, consentendo ai client di eseguire le operazioni necessarie senza che l'applicazione debba gestire direttamente il trasferimento dei dati. In questo modo si eliminano il sovraccarico di elaborazione e l'impatto su prestazioni e scalabilità dall'applicazione e dal server.

Il client usa questo token per accedere a una risorsa specifica nell'archivio dati solo per un periodo di tempo e con restrizioni specifiche sulle autorizzazioni di accesso, come illustrato nella figura. Dopo il periodo specificato, la chiave non sarà più valida e non consentirà l'accesso alla risorsa.

Diagramma di un tipico flusso di lavoro del modello di chiavi di controllo.

Diagramma che mostra un esempio del flusso di lavoro per un sistema che usa il modello di chiave di controllo. Il passaggio 1 mostra l'utente che richiede la risorsa di destinazione. Il passaggio 2 mostra l'applicazione di passeggio verificando la validità della richiesta e generando un token di accesso. Il passaggio 3 mostra il token restituito all'utente. Il passaggio 4 mostra l'utente che accede alla risorsa di destinazione usando il token.

È anche possibile configurare una chiave con altre dipendenze, ad esempio l'ambito dei dati. Ad esempio, a seconda delle funzionalità dell'archivio dati, la chiave può specificare un'intera tabella in un archivio dati o solo le righe specifiche di una tabella. Nei sistemi di archiviazione cloud la chiave può specificare un contenitore o solo un elemento specifico al suo interno.

La chiave può anche essere invalidata dall'applicazione. Si tratta di un approccio utile se il client segnala al server che l'operazione di trasferimento dei dati è stata completata. Il server può poi invalidare la chiave per impedire ulteriori accessi.

L'uso di questo modello può semplificare la gestione dell'accesso alle risorse perché non è necessario creare e autenticare un utente, concedere autorizzazioni e quindi rimuovere di nuovo l'utente o, peggio, lasciare tale autorizzazione come autorizzazione permanente. Consente inoltre di limitare facilmente la posizione, l'autorizzazione e il periodo di validità, generando semplicemente una chiave in fase di esecuzione. I fattori importanti consistono nel limitare il periodo di validità, e in particolare la posizione della risorsa, quanto più possibile per garantire che il destinatario possa usarla solo per lo scopo previsto.

Considerazioni e problemi

Prima di decidere come implementare questo modello, considerare quanto segue:

Gestire lo stato e il periodo di validità della chiave. Se persa o compromessa, la chiave sblocca l'elemento di destinazione e lo rende disponibile per uso dannoso durante il periodo di validità. Una chiave può essere in genere revocata o disabilitata, a seconda della modalità di emissione. I criteri lato server possono essere modificati oppure la chiave del server con cui è stato firmato può essere invalidata. Specificare un periodo di validità breve per ridurre al minimo il rischio di consentire operazioni non autorizzate nell'archivio dati. Se tuttavia il periodo di validità è troppo breve, il client potrebbe non essere in grado di completare l'operazione prima della scadenza della chiave. Consentire agli utenti autorizzati di rinnovare la chiave prima della scadenza del periodo di validità se sono necessari più accessi alla risorsa protetta.

Controllare il livello di accesso fornito dalla chiave. La chiave deve in genere consentire all'utente di eseguire solo le azioni necessarie per completare l'operazione, ad esempio l'accesso di sola lettura se il client non è in grado di caricare dati nell'archivio dati. Per l'upload dei file viene in genere specificata una chiave che fornisce l'autorizzazione di sola scrittura, nonché il percorso e il periodo di validità. È fondamentale specificare in modo accurato la risorsa o il set di risorse a cui si applica la chiave.

Valutare come controllare il comportamento degli utenti. L'implementazione di questo modello implica una perdita parziale di controllo sulle risorse a cui gli utenti sono autorizzati ad accedere. Il livello di controllo che può essere esercitato è limitato dalle funzionalità dei criteri e delle autorizzazioni disponibili per il servizio o l'archivio dati di destinazione. Ad esempio, in genere non è possibile creare una chiave che limita le dimensioni dei dati da scrivere nell'archiviazione o il numero di volte in cui è possibile usare la chiave per accedere a un file. Questo problema può determinare elevati costi imprevisti per il trasferimento dei dati, anche se in uso da parte del client previsto, e può essere dovuto a un errore nel codice che determina upload e download ripetuti. Per limitare il numero di volte in cui può essere caricato un file, laddove possibile, forzare il client a inviare una notifica all'applicazione quando un'operazione è stata completata. Alcuni archivi dati generano ad esempio eventi che il codice dell'applicazione può usare per monitorare le operazioni e controllare il comportamento degli utenti. Tuttavia, è difficile applicare quote per i singoli utenti in uno scenario multi-tenant in cui la stessa chiave viene usata da tutti gli utenti di un tenant. La concessione di autorizzazioni di creazione degli utenti consente di controllare la quantità di dati aggiornati rendendo i token in modo efficace a uso singolo. L'autorizzazione di creazione non consente sovrascrizioni, quindi ogni token può essere usato solo per un'attività di scrittura.

Convalidare e facoltativamente purificare tutti i dati caricati. Un utente malintenzionato che riesce ad accedere alla chiave potrebbe caricare dati progettati per compromettere il sistema. In alternativa, gli utenti autorizzati potrebbero caricare dati non validi che, se elaborati, potrebbero generare un errore o un errore di sistema. Per evitare questo problema, verificare che tutti i dati caricati siano convalidati e non contengano contenuto dannoso prima dell'uso.

Controllare tutte le operazioni. Molti meccanismi basati su chiavi possono registrare alcune operazioni, ad esempio gli upload, i download e gli errori. Questi log possono essere in genere incorporati in un processo di controllo e usati anche per la fatturazione se gli addebiti dell'utente variano in base alle dimensioni dei file o al volume di dati. Usare i log per rilevare errori di autenticazione che potrebbero essere causati da problemi con il provider della chiave o la rimozione accidentale di criteri di accesso archiviati.

Distribuire in modo sicuro la chiave. Può essere incorporata in un URL attivato dall'utente in una pagina Web o può essere usato in un'operazione di reindirizzamento del server per consentire il download automatico. Usare sempre HTTPS per distribuire la chiave su un canale sicuro.

Proteggere i dati sensibili in transito. I dati sensibili recapitati tramite l'applicazione in genere verranno distribuiti tramite TLS e questo deve essere applicato per i client che accedono direttamente all'archivio dati.

Di seguito sono riportati altri problemi da considerare quando si implementa questo modello:

  • Se il client non segnala al server il completamento dell'operazione, o non è in grado di farlo, e l'unico limite è il periodo di scadenza della chiave, l'applicazione non sarà in grado di eseguire le operazioni di controllo, ad esempio il conteggio del numero di upload o download o la capacità di impedire più upload o download.

  • La flessibilità dei criteri delle chiavi che è possibile generare potrebbe essere limitata. Alcuni meccanismi consentono ad esempio solo l'uso di un periodo di scadenza a tempo. Altri non sono in grado di specificare un livello sufficiente di granularità per le autorizzazioni di lettura/scrittura.

  • Se viene specificata l'ora di inizio per la chiave o il periodo di validità del token, assicurarsi che sia leggermente anteriore all'ora corrente, poiché gli orologi dei client potrebbero non essere perfettamente sincronizzati. Il valore predefinito, se non specificato, è in genere l'ora del server corrente.

  • L'URL contenente la chiave potrebbe essere registrato nei file di log del server. Sebbene in genere la chiave scada prima di usare i file di log per l'analisi, assicurarsi comunque di limitare l'accesso a questi file. Se i dati di log vengono trasmessi a un sistema di monitoraggio o archiviati in un'altra posizione, è consigliabile implementare un ritardo per impedire la perdita delle chiavi dopo la scadenza del periodo di validità.

  • Se il codice del client viene eseguito in un Web browser, il browser potrebbe dover supportare la condivisione di risorse tra origini (CORS) per consentire al codice in esecuzione nel Web browser di accedere ai dati in un dominio diverso da quello che ha gestito la pagina. Alcuni browser meno recenti e alcuni archivi dati non supportano CORS e il codice eseguito in questi browser potrebbe non essere in grado di usare una chiave di controllo per fornire l'accesso ai dati in un dominio diverso, ad esempio un account di archiviazione cloud.

  • Anche se il client non deve avere l'autenticazione preconfigurato per la risorsa finale, il client deve preestablire mezzi di autenticazione per il servizio di chiavi di controllo.

  • Le chiavi devono essere distribuite solo ai client autenticati con autorizzazione appropriata.

  • La generazione di token di accesso è un'azione privilegiata, quindi il servizio di chiavi di controllo deve essere protetto con criteri di accesso rigorosi. Il servizio può consentire l'accesso a sistemi sensibili da terze parti, rendendo la sicurezza di questo servizio di particolare importanza.

Quando usare questo modello

Questo modello è utile nelle situazioni seguenti:

  • Per ridurre al minimo il caricamento delle risorse e ottimizzare prestazioni e scalabilità. L'uso di un passepartout non prevede il blocco della risorsa, non è necessario eseguire una chiamata al server remoto, non sussistono limiti al numero di passepartout che è possibile emettere ed evita che si verifichi un singolo punto di guasto risultante dall'esecuzione del trasferimento dei dati tramite il codice dell'applicazione. La creazione di un passepartout consiste in genere in una semplice operazione crittografica di firma di una stringa con una chiave.

  • Per ridurre al minimo i costi operativi. L'accesso diretto ad archivi e risorse consente di risparmiare tempo e denaro, comporta un numero inferiore di round trip di rete e potrebbe determinare una riduzione nel numero delle risorse di calcolo necessarie.

  • Quando i client periodicamente caricano o scaricano dati, in particolare in presenza di volumi elevati o se ogni operazione riguarda file di grandi dimensioni.

  • Quando l'applicazione ha un numero limitato di risorse di calcolo disponibili, a causa di limitazioni di hosting o per motivi economici. In questo scenario, il modello risulta particolarmente utile in presenza di vari upload o download simultanei, poiché evita all'applicazione di gestire il trasferimento dei dati.

  • Quando i dati vengono archiviati in un archivio dati remoto o in un'area diversa. Se l'applicazione doveva fungere da gatekeeper, potrebbe essere previsto un addebito per la larghezza di banda aggiuntiva di trasferimento dei dati tra aree o tra reti pubbliche o private tra il client e l'applicazione e quindi tra l'applicazione e l'archivio dati.

Questo modello può non essere utile nelle situazioni seguenti:

  • Se i client possono già eseguire l'autenticazione univoca al servizio back-end, con il controllo degli accessi in base al ruolo, ad esempio, non usare questo modello.

  • Se l'applicazione deve eseguire alcune attività sui dati prima di che vengano archiviati o inviati al client. Se ad esempio l'applicazione deve eseguire la convalida, registrare l'esito positivo dell'accesso o eseguire una trasformazione sui dati. Tuttavia, alcuni archivi dati e client sono in grado di negoziare ed eseguire trasformazioni semplici, ad esempio compressione e decompressione (ad esempio, un Web browser può gestire i formati gzip).

  • Se la progettazione di un'applicazione esistente rende difficile incorporare il modello. L'uso di questo modello comporta in genere un approccio diverso dal punto di vista dell'architettura per il recapito e la ricezione di dati.

  • Se è necessario gestire audit trail o controllare il numero di tentativi di esecuzione di un'operazione di trasferimento di dati e il meccanismo di passepartout in uso non supporta le notifiche che il server può usare per gestire queste operazioni.

  • Se è necessario limitare le dimensioni dei dati, soprattutto durante le operazioni di upload. L'unica soluzione per questo scenario prevede che l'applicazione controlli le dimensioni dei dati al termine dell'operazione oppure le dimensioni degli upload dopo un periodo specifico o a intervalli regolari.

Progettazione del carico di lavoro

Un architetto deve valutare il modo in cui il modello Di passeggino può essere usato nella progettazione del carico di lavoro per soddisfare gli obiettivi e i principi trattati nei pilastri di Azure Well-Architected Framework. Ad esempio:

Concetto fondamentale Come questo modello supporta gli obiettivi di pilastro
Le decisioni di progettazione della sicurezza consentono di garantire la riservatezza, l'integrità e la disponibilità dei dati e dei sistemi del carico di lavoro. Questo modello consente a un client di accedere direttamente a una risorsa senza bisogno di credenziali permanenti o di lunga durata. Tutte le richieste di accesso iniziano con una transazione controllabile. L'accesso concesso è quindi limitato sia nell'ambito che nella durata. Questo modello semplifica anche la revoca dell'accesso concesso.

- SE:05 Gestione delle identità e degli accessi
L'ottimizzazione dei costi è incentrata sul mantenimento e sul miglioramento del ritorno del carico di lavoro sugli investimenti. Questa progettazione esegue l'offload dell'elaborazione come relazione esclusiva tra il client e la risorsa senza aggiungere un componente per gestire direttamente tutte le richieste client. Il vantaggio è più significativo quando le richieste client sono frequenti o sufficientemente grandi da richiedere risorse proxy significative.

- Costi del flusso CO:09
L'efficienza delle prestazioni consente al carico di lavoro di soddisfare in modo efficiente le richieste tramite ottimizzazioni in termini di scalabilità, dati, codice. Non usando una risorsa intermedia per delegare l'elaborazione dell'accesso come relazione esclusiva tra il client e la risorsa senza richiedere un componente ambassador che deve gestire tutte le richieste client in modo efficiente. Il vantaggio dell'uso di questo modello è più significativo quando il proxy non aggiunge valore alla transazione.

- PE:07 Codice e infrastruttura

Come per qualsiasi decisione di progettazione, prendere in considerazione eventuali compromessi rispetto agli obiettivi degli altri pilastri che potrebbero essere introdotti con questo modello.

Esempio

Azure supporta firme di accesso condiviso in Archiviazione di Azure per il controllo dell'accesso granulare ai dati in BLOB, tabelle e code e per le code e gli argomenti del bus di servizio. È possibile configurare un token di firma di accesso condiviso per concedere specifici diritti di accesso, ad esempio lettura, scrittura, aggiornamento ed eliminazione, a una specifica tabella, un intervallo di chiavi in una tabella, una coda, un BLOB o un contenitore BLOB. La validità può essere un periodo di tempo specificato. Questa funzionalità è particolarmente adatta per l'uso di una chiave di controllo per l'accesso.

Si consideri un carico di lavoro con centinaia di client per dispositivi mobili o desktop che caricano spesso file binari di grandi dimensioni. Senza questo modello, il carico di lavoro ha essenzialmente due opzioni. Il primo consiste nel fornire l'accesso permanente e la configurazione a tutti i client per eseguire i caricamenti direttamente in un account di archiviazione. L'altro consiste nell'implementare il modello di routing del gateway per configurare un endpoint in cui i client usano l'accesso proxy all'archiviazione, ma questo potrebbe non aggiungere valore aggiuntivo alla transazione. Entrambi gli approcci soffrono di problemi risolti nel contesto del modello:

  • Segreti precondivisi di lunga durata. Potenzialmente senza molto modo per fornire chiavi diverse a client diversi.
  • Sono state aggiunte spese per l'esecuzione di un servizio di calcolo con risorse sufficienti per gestire la ricezione di file di grandi dimensioni.
  • Rallentare potenzialmente le interazioni client aggiungendo un ulteriore livello di calcolo e hop di rete al processo di caricamento.

L'uso del modello Di passeparte consente di risolvere i problemi di sicurezza, ottimizzazione dei costi e prestazioni.

Diagramma che mostra un client che accede a un account di archiviazione dopo aver ottenuto un token di accesso da un'API.

  1. I client, all'ultimo momento responsabile, eseguono l'autenticazione a un'API ospitata dalla funzione di Azure leggera a zero per richiedere l'accesso.

  2. L'API convalida la richiesta e quindi ottiene e restituisce un token SaS con ambito e ora limitato.

    Il token generato dall'API limita il client alle limitazioni seguenti:

    • Account di archiviazione da usare. Ciò significa che il client non deve conoscere queste informazioni in anticipo.
    • Contenitore e nome file specifici da usare; garantire che il token possa essere usato al massimo con un file.
    • Breve finestra di funzionamento, ad esempio tre minuti. Questo breve periodo di tempo garantisce che i token abbiano una durata (TTL) che non si estende oltre la relativa utilità.
    • Autorizzazioni solo per creare un BLOB, non scaricare, aggiornare o eliminare.
  3. Tale token viene quindi usato dal client, all'interno dell'intervallo di tempo ristretto, per caricare il file direttamente nell'account di archiviazione.

L'API genera questi token ai client autorizzati usando una chiave di delega utente in base all'identità gestita dell'ID Microsoft Entra dell'API. La registrazione è abilitata sia negli account di archiviazione che nell'API di generazione del token consentono la correlazione tra le richieste di token e l'utilizzo dei token. L'API può usare le informazioni di autenticazione client o altri dati disponibili per decidere quale account di archiviazione o contenitore usare, ad esempio in una situazione multi-tenant.

Un esempio completo è disponibile in GitHub nell'esempio di modello di passeparte key. I frammenti di codice seguenti vengono adattati da questo esempio. In questo primo caso viene illustrato come la funzione di Azure (disponibile in ValetKey.Web) generi un token di firma di accesso condiviso delegato dall'utente usando la propria identità gestita della funzione di Azure.

[Function("FileServices")]
public async Task<StorageEntitySas> GenerateTokenAsync([HttpTrigger(...)] HttpRequestData req, ..., 
                                                        CancellationToken cancellationToken)
{
  // Authorize the caller, select a blob storage account, container, and file name.
  // Authenticate to the storage account with the Azure Function's managed identity.
  ...

  return await GetSharedAccessReferenceForUploadAsync(blobContainerClient, blobName, cancellationToken);
}

/// <summary>
/// Return an access key that allows the caller to upload a blob to this
/// specific destination for about three minutes.
/// </summary>
private async Task<StorageEntitySas> GetSharedAccessReferenceForUploadAsync(BlobContainerClient blobContainerClient, 
                                                                            string blobName,
                                                                            CancellationToken cancellationToken)
{
  var blobServiceClient = blobContainerClient.GetParentBlobServiceClient();
  var blobClient = blobContainerClient.GetBlockBlobClient(blobName);

  // Allows generating a SaS token that is evaluated as the union of the RBAC permissions on the managed identity
  // (for example, Blob Data Contributor) and then narrowed further by the specific permissions in the SaS token.
  var userDelegationKey = await blobServiceClient.GetUserDelegationKeyAsync(DateTimeOffset.UtcNow.AddMinutes(-3),
                                                                            DateTimeOffset.UtcNow.AddMinutes(3),
                                                                            cancellationToken);

  // Limit the scope of this SaS token to the following:
  var blobSasBuilder = new BlobSasBuilder
  {
      BlobContainerName = blobContainerClient.Name,     // - Specific container
      BlobName = blobClient.Name,                       // - Specific filename
      Resource = "b",                                   // - Blob only
      StartsOn = DateTimeOffset.UtcNow.AddMinutes(-3),  // - For about three minutes (+/- for clock drift)
      ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(3),  // - For about three minutes (+/- for clock drift)
      Protocol = SasProtocol.Https                      // - Over HTTPS
  };
  blobSasBuilder.SetPermissions(BlobSasPermissions.Create);

  return new StorageEntitySas
  {
      BlobUri = blobClient.Uri,
      Signature = blobSasBuilder.ToSasQueryParameters(userDelegationKey, blobServiceClient.AccountName).ToString();
  };
}

Il frammento di codice seguente è l'oggetto DTO (Data Transfer Object) usato sia dall'API che dal client.

public class StorageEntitySas
{
  public Uri? BlobUri { get; internal set; }
  public string? Signature { get; internal set; }
}

Il client (disponibile in ValetKey.Client) usa quindi l'URI e il token restituiti dall'API per eseguire il caricamento senza richiedere risorse aggiuntive e prestazioni complete da client a archiviazione.

...

// Get the SaS token (valet key)
var blobSas = await httpClient.GetFromJsonAsync<StorageEntitySas>(tokenServiceEndpoint);
var sasUri = new UriBuilder(blobSas.BlobUri)
{
    Query = blobSas.Signature
};

// Create a blob client using the SaS token as credentials
var blob = new BlobClient(sasUri.Uri);

// Upload the file directly to blob storage
using (var stream = await GetFileToUploadAsync(cancellationToken))
{
    await blob.UploadAsync(stream, cancellationToken);
}

...

Passaggi successivi

Le indicazioni seguenti potrebbero essere rilevanti per l'implementazione di questo modello:

Quando si implementa questo modello, possono essere rilevanti anche i modelli seguenti:

  • Modello Gatekeeper. Questo modello può essere usato insieme al modello Passepartout per proteggere le applicazioni e i servizi usando un'istanza host dedicata che funga da broker tra i client e l'applicazione o il servizio. Il gatekeeper convalida e purifica le richieste e passa richieste e dati tra il client e l'applicazione. Può conferire un livello aggiuntivo di sicurezza e ridurre la superficie di attacco del sistema.
  • Modello di hosting del contenuto statico. Descrive come distribuire risorse statiche in un servizio di archiviazione basato su cloud in grado di recapitare queste risorse direttamente al client per ridurre l'esigenza di dispendiose istanze di calcolo. Laddove le risorse non siano destinate a essere rese disponibili pubblicamente, il modello di passepartout può essere usato per proteggerle.