代客金鑰模式

Azure
Azure 儲存體

使用令牌,提供用戶端對特定資源的受限直接存取,以便從應用程式卸除數據傳輸。 這對於使用雲端裝載記憶體系統或佇列的應用程式特別有用,而且可將成本降至最低,並最大化延展性和效能。

內容和問題

用戶端程式和網頁瀏覽器通常需要讀取和寫入應用程式記憶體的檔案或數據流。 一般而言,應用程式會藉由從記憶體擷取數據並將其串流至用戶端,或從用戶端讀取上傳的數據流並將它儲存在數據存放區,來處理數據的移動。 不過,此方法會吸收寶貴的資源,例如計算、記憶體和頻寬。

數據存放區能夠直接處理數據的上傳和下載,而不需要應用程式執行任何處理來移動此數據。 但是,這通常需要用戶端存取存放區的安全性認證。 這可以是一種實用的技術,可將數據傳輸成本降到最低,以及相應放大應用程式的需求,以及將效能最大化。 不過,這表示應用程式無法再管理數據的安全性。 用戶端連線到數據存放區以進行直接存取之後,應用程式就無法做為閘道守衛。 它不再控制此程式,而且無法防止後續從數據存放區上傳或下載。

在需要為不受信任的用戶端提供服務的分散式系統中,這不是現實的方法。 相反地,應用程式必須能夠以細微的方式安全地控制數據的存取,但仍可藉由設定此連線來減少伺服器上的負載,然後允許用戶端直接與數據存放區通訊,以執行必要的讀取或寫入作業。

解決方案

您必須解決控制數據存放區的存取問題,而存放區無法管理客戶端的驗證和授權。 一個典型的解決方案是限制存取資料存放區的公用連線,並提供數據存放區可以驗證的密鑰或令牌給用戶端。

此金鑰或令牌通常稱為代客金鑰。 它提供特定資源的限時存取,只允許預先定義的作業,例如讀取和寫入記憶體或佇列,或在網頁瀏覽器中上傳和下載。 應用程式可以快速且輕鬆地建立和發出用戶端裝置和網頁瀏覽器的代客密鑰,讓用戶端執行必要的作業,而不需要應用程式直接處理數據傳輸。 這會從應用程式和伺服器中移除處理額外負荷,以及對效能和延展性的影響。

用戶端只會使用此令牌來存取數據存放區中的特定資源,並具有訪問許可權的特定限制,如圖所示。 在指定的期間之後,金鑰會變成無效,且不允許存取資源。

圖 1 - 模式概觀

您也可以設定具有其他相依性的密鑰,例如數據的範圍。 例如,根據數據存放區功能,索引鍵可以在數據存放區中指定完整的數據表,或只指定數據表中的特定數據列。 在雲端記憶體系統中,密鑰可以指定容器,或只指定容器內的特定專案。

應用程式也可以讓金鑰失效。 如果用戶端通知伺服器數據傳輸作業已完成,這是一個有用的方法。 然後伺服器可以將該金鑰失效,以防止進一步存取。

使用此模式可以簡化資源的管理存取,因為不需要建立和驗證使用者、授與許可權,然後再移除使用者。 它也可讓您輕鬆地限制位置、許可權和有效期間,只要在運行時間產生密鑰即可。 重要因素是盡可能嚴格地限制有效期間,特別是資源的位置,以便收件者只能將它用於預定目的。

問題和考慮

決定如何實作此模式時,請考慮下列幾點:

管理金鑰的有效性狀態和期間。 如果洩漏或遭入侵,密鑰會有效解除鎖定目標專案,並在有效期間提供惡意使用。 根據金鑰的發出方式,通常可以撤銷或停用金鑰。 伺服器端原則可以變更,或者,其簽署的伺服器密鑰可能會失效。 指定較短的有效期間,以將允許未經授權的作業對數據存放區進行的風險降到最低。 不過,如果有效期間太短,用戶端可能無法在密鑰到期之前完成作業。 如果需要多個受保護資源的存取權,允許授權使用者在有效期間到期之前更新密鑰。

控制金鑰將提供的存取層級。 一般而言,密鑰應該允許使用者只執行完成作業所需的動作,例如,如果客戶端不應該將數據上傳至數據存放區,則為只讀存取。 對於檔案上傳,通常指定提供唯寫許可權的密鑰,以及位置和有效期間。 請務必正確指定套用金鑰的資源或資源集。

請考慮如何控制用戶的行為。 實作此模式表示會授與使用者存取權資源的某些控制權。 可以發揮的控制層級受限於服務或目標數據存放區可用的原則和許可權的功能。 例如,通常無法建立密鑰來限制寫入記憶體的數據大小,或密鑰可用來存取檔案的次數。 這可能會導致數據傳輸的巨大非預期成本,即使預期用戶端使用,也可能是因為程式代碼中導致重複上傳或下載的錯誤所造成。 若要限制可以上傳檔案的次數,請儘可能強制用戶端在一項作業完成時通知應用程式。 例如,某些數據存放區會引發應用程式程式代碼可用來監視作業和控制用戶行為的事件。 不過,在多租使用者案例中,很難為個別使用者強制執行配額,其中所有使用者都使用同一個租使用者中的相同密鑰。

驗證並選擇性地清理所有上傳的數據。 取得金鑰存取權的惡意使用者可能會上傳設計來危害系統的數據。 或者,授權的使用者可能會上傳無效的數據,而且處理時可能會導致錯誤或系統失敗。 若要防範此情況,請確定所有上傳的數據都會經過驗證,並在使用之前檢查是否有惡意內容。

稽核所有作業。 許多金鑰型機制都可以記錄作業,例如上傳、下載和失敗。 這些記錄通常可以併入稽核程式,而且如果使用者根據檔案大小或數據量收費,也會用於計費。 使用記錄來偵測因密鑰提供者問題或意外移除預存存取原則所造成的驗證失敗。

安全地傳遞金鑰。 它可以內嵌在使用者在網頁中啟動的 URL 中,也可以用於伺服器重新導向作業,以便自動下載。 一律使用 HTTPS 透過安全通道傳遞金鑰。

保護傳輸中的敏感數據。 透過應用程式傳遞的敏感數據通常會使用 TLS 進行,這應該針對直接存取資料存放區的用戶端強制執行。

實作此模式時要注意的其他問題如下:

  • 如果客戶端沒有或無法通知伺服器完成作業,且唯一的限制是金鑰的到期期間,應用程式將無法執行稽核作業,例如計算上傳或下載次數,或防止多次上傳或下載。

  • 可產生之金鑰原則的彈性可能會受到限制。 例如,某些機制只允許使用計時到期期間。 其他則無法指定讀取/寫入許可權的足夠粒度。

  • 如果指定金鑰或令牌有效期間開始時間,請確定它稍早於目前的伺服器時間,以允許用戶端時鐘稍微超出同步處理。 如果未指定,則預設值通常是目前的伺服器時間。

  • 包含金鑰的 URL 將會記錄在伺服器記錄檔中。 雖然密鑰通常會在記錄檔用於分析之前過期,但請確定您限制存取它們。 如果記錄數據傳輸至監視系統或儲存在另一個位置,請考慮實作延遲以防止密鑰外洩,直到其有效期間到期為止。

  • 如果用戶端程式代碼在網頁瀏覽器中執行,瀏覽器可能需要支援跨原始來源資源分享 (CORS),才能讓網頁瀏覽器中執行的程式代碼,以從提供頁面的不同網域存取數據。 某些較舊的瀏覽器和某些資料存放區不支援 CORS,而且在這些瀏覽器中執行的程式代碼可能無法使用代客密鑰來提供不同網域數據的存取權,例如雲端記憶體帳戶。

使用此模式的時機

此模式適用於下列情況:

  • 若要將資源載入降到最低,並將效能和延展性最大化。 使用代客金鑰不需要鎖定資源、不需要遠端伺服器呼叫、發出代客密鑰數目沒有限制,而且可避免透過應用程式程式代碼執行資料傳輸所造成的單一失敗點。 建立代客金鑰通常是使用金鑰簽署字串的簡單密碼編譯作業。

  • 若要將營運成本降到最低。 啟用對存放區和佇列的直接存取具有資源和成本效益,可能會導致網路來回行程較少,而且可能會減少所需的計算資源數目。

  • 當用戶端定期上傳或下載數據時,特別是當有大量或每個作業牽涉到大型檔案時。

  • 當應用程式可用的計算資源有限時,可能是因為裝載限制或成本考慮。 在此案例中,如果有許多並行數據上傳或下載,模式會更有用,因為它可讓應用程式無法處理數據傳輸。

  • 當資料儲存在遠端資料存放區或不同的資料中心時。 如果應用程式需要作為閘道守衛,則可能需要額外的頻寬,以在資料中心之間傳輸數據,或在用戶端與應用程式之間的公用或專用網之間,然後在應用程式與數據存放區之間傳輸數據。

在下列情況下,此模式可能沒有用處:

  • 如果應用程式在儲存資料之前或傳送至用戶端之前,必須先對數據執行一些工作。 例如,如果應用程式需要執行驗證、記錄存取成功,或對數據執行轉換。 不過,某些數據存放區和客戶端能夠交涉並執行簡單的轉換,例如壓縮和解壓縮(例如,網頁瀏覽器通常可以處理 gzip 格式)。

  • 如果現有應用程式的設計難以納入模式。 使用此模式通常需要不同的架構方法來傳遞和接收數據。

  • 如果需要維護稽核線索或控制執行數據傳輸作業的次數,且使用中的代客密鑰機制不支援伺服器可用來管理這些作業的通知。

  • 如果需要限制數據的大小,特別是在上傳作業期間。 唯一的解決方案是應用程式在作業完成之後檢查數據大小,或檢查在指定期間或排程之後上傳的大小。

工作負載設計

架構設計人員應該評估在工作負載的設計中如何使用代客密鑰模式,以解決 Azure 良好架構架構支柱涵蓋的目標和原則。 例如:

要素 此模式如何支援支柱目標
安全性 設計決策有助於確保 工作負載數據和系統的機密性完整性可用性 此模式可讓用戶端直接存取資源,而不需要長期或常設認證。 所有存取要求都是從可稽核的交易開始。 然後授與的存取權會同時限制在範圍和持續時間內。 此模式也可讓您更輕鬆地撤銷授與的存取權。

- SE:05 身分識別和存取管理
成本優化著重於維持和改善工作負載的投資報酬率。 此設計會將處理卸除為用戶端與資源之間的獨佔關聯性,而不需要新增元件即可直接處理所有用戶端要求。 當用戶端要求頻繁或夠大而需要大量 Proxy 資源時,優點最為顯著。

- CO:09 流程成本
效能效率 可透過調整、數據、程式代碼的優化,有效率地協助您的工作負載 符合需求 不使用中繼資源來 Proxy 存取卸除處理做為用戶端與資源之間的獨佔關聯性,而不需要以高效能方式處理所有用戶端要求的大使元件。 當 Proxy 不會將值新增至交易時,使用此模式的優點最為顯著。

- PE:07 程式代碼和基礎結構

如同任何設計決策,請考慮對其他可能以此模式導入之目標的任何取捨。

範例

Azure 支援 Azure 儲存體 上的共用存取簽章,以便對 Blob、數據表和佇列中的數據進行細微訪問控制,以及 服務匯流排 佇列和主題。 共用存取簽章令牌可以設定為提供特定訪問許可權,例如讀取、寫入、更新和刪除特定數據表;數據表內的索引鍵範圍;佇列;Blob;或 Blob 容器。 有效性可以是指定的時間週期。 這項功能非常適合使用代客密鑰進行存取。

請考慮具有數百個行動或桌面用戶端經常上傳大型二進位檔的工作負載。 如果沒有此模式,工作負載基本上有兩個選項。 第一個是為所有用戶端提供常設存取和組態,以直接執行上傳至記憶體帳戶。 另一個是實 作網關路由模式 ,以設定用戶端使用 Proxy 存取記憶體的端點,但這可能不會將額外的值新增至交易。 這兩種方法都會在模式內容中遇到問題:

  • 長時間存留,預先共用的秘密。 可能沒有太多方法可為不同的用戶端提供不同的密鑰。
  • 已新增執行計算服務的費用,此服務有足夠的資源來處理目前接收大型檔案。
  • 藉由將額外的計算和網路躍點新增至上傳程式,可能會減緩客戶端互動的速度。

使用代客金鑰模式可解決安全性、成本優化和效能考慮。

此圖顯示用戶端在第一次從 API 取得存取令牌之後存取記憶體帳戶。

  1. 用戶端在最後一個負責的時刻,向輕量、向零調整為零的 Azure 函式裝載 API 進行驗證,以要求存取。

  2. API 會驗證要求,然後取得並傳回時間與範圍有限的 SaS 令牌。

    API 所產生的令牌會將用戶端限制為下列限制:

    • 要使用的記憶體帳戶。 也就是說,用戶端不需要事先知道這項資訊。
    • 要使用的特定容器和檔名;確保令牌最多可以搭配一個檔案使用。
    • 一個簡短的作業視窗,例如三分鐘。 這個簡短的時間週期可確保令牌具有不會超過其公用程式的TTL。
    • 建立 Blob 的許可權,而不是下載、更新或刪除。
  3. 客戶端接著會在狹窄的時間範圍內使用該令牌,直接將檔案上傳至記憶體帳戶。

API 會根據 API 自己的 Microsoft Entra ID 受控識別,使用 使用者委派密鑰 ,將這些令牌產生給授權用戶端。 在記憶體帳戶和令牌產生 API 上啟用記錄,允許令牌要求與令牌使用之間的相互關聯。 API 可以使用用戶端驗證資訊或其他數據,以決定要使用的記憶體帳戶或容器,例如在多租用戶的情況下。

GitHub 上提供完整的範例,位於 代客密鑰模式範例。 下列代碼段會根據該範例進行調整。 第一個示範如何使用 Azure Function 自己的受控識別來產生使用者委派的共用存取簽章令牌,說明 Azure 函式 (在代客密鑰.Web 中找到) 如何產生使用者委派的共用存取簽章令牌。

[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();
  };
}

下列代碼段是 API 和用戶端所使用的數據傳輸物件 (DTO)。

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

用戶端 (在 CryptographicKey.Client 中找到),然後使用從 API 傳回的 URI 和令牌來執行上傳,而不需要額外的資源和完整的客戶端對記憶體效能。

...

// 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);
}

...

下一步

實作此模式時,下列指引可能相關:

實作此模式時,下列模式也可能相關:

  • 閘道守衛模式。 此模式可以與代客密鑰模式搭配使用,藉由使用作為用戶端與應用程式或服務之間訊息代理程式的專用主機實例來保護應用程式和服務。 閘道守衛會驗證並清理要求,並在用戶端與應用程式之間傳遞要求和數據。 可以提供額外的安全性層級,並減少系統的受攻擊面。
  • 靜態內容裝載模式。 描述如何將靜態資源部署到雲端式記憶體服務,以將這些資源直接傳遞給用戶端,以減少對昂貴計算實例的需求。 如果資源不適合公開使用,則代客密鑰模式可用來保護它們。