共用方式為


使用共用存取簽章 (SAS) 驗證對事件中樞資源的存取

共用存取簽章 (SAS) 可讓您更精確地對用戶端控制授與的存取類型。 以下是您可以在 SAS 中設定的一些控制項:

  • SAS 的有效期間,包括開始時間和到期時間。
  • SAS 所授與的權限。 例如,事件中樞命名空間的 SAS 可能會授與接聽權限,但不授與傳送權限。
  • 只有出示有效認證的用戶端才能將資料傳送到事件中樞。
  • 一個用戶端無法模擬另一個用戶端。
  • 可封鎖惡意用戶端,讓它無法將資料傳送到事件中樞。

本文涵蓋使用 SAS 驗證對事件中樞資源的存取。 若要了解如何使用 SAS 來授權對事件中樞資源的存取,請參閱此文章

注意

我們建議您的安全性最佳做法是盡可能使用 Microsoft Entra 認證,而不是使用共用存取簽章,因為後者可能更容易遭到盜用。 您可以繼續使用共用存取簽章 (SAS) 將細部存取權授與事件中樞資源,但 Microsoft Entra ID 提供類似功能,卻不必管理 SAS 權杖或擔心需要撤銷遭盜用的 SAS。

如需 Azure 事件中樞中 Microsoft Entra 整合的詳細資訊,請參閱使用 Microsoft Entra 授權對事件中樞的存取

設定 SAS 驗證

您可以在事件中樞命名空間或實體 (事件中樞或 Kafka 主題) 上設定 SAS 規則。 目前不支援在取用者群組上設定 SAS 規則,但您可以使用在命名空間或實體上設定的規則來保護對取用者群組的存取。 下圖顯示授權規則如何適用範例實體。

此圖顯示具有接聽、傳送和管理規則的事件中樞。

在此範例中,範例事件中樞命名空間 (ExampleNamespace) 有兩個實體:eh1 和 Kafka topic1。 授權規則是在實體層級以及命名空間層級定義。

manageRuleNS、sendRuleNS 和 listenRuleNS 授權規則都適用於 eh1 和 topic1。 listenRule-eh 和 sendRule-eh 授權規則只適用 eh1,而 sendRuleT 授權規則只適用 topic1。

使用 sendRuleNS 授權規則時,用戶端應用程式可以傳送至 eh1 和 topic1 兩者。 使用 sendRuleT 授權規則時,它只會強制執行對 topic1 的細微存取,因此使用此規則來存取的用戶端應用程式,現在無法傳送至 eh1,而只能傳送至 topic1。

產生共用存取簽章權杖

任何有權存取授權規則名稱及其簽署金鑰之一的用戶端,都可以產生 SAS 權杖。 權杖是以下列格式編寫字串而產生的:

  • se - 權杖到期時間。 此整數反映自 Epoch 1970 年 1 月 1 日 00:00:00 UTC (UNIX Epoch) 起到權杖到期時所經過的秒數
  • skn - 授權規則的名稱,也就是 SAS 金鑰名稱。
  • sr - 所存取資源的 URI。
  • sig - 簽章。

signature-string 是對資源 URI 計算的 SHA-256 雜湊 (範圍如上一節描述) 和權杖到期時間的字串表示 (以歸位字元和換行字元 (CRLF) 分隔)。 雜湊計算看起來類似下列虛擬程式碼,會傳回 256 位元/32 位元組的雜湊值。

SHA-256('https://<yournamespace>.servicebus.windows.net/'+'\n'+ 1438205742)

權杖會包含非雜湊值,因此接收者可以用相同的參數重新計算雜湊,驗證簽發者擁有有效的簽署金鑰。

資源 URI 是宣告其存取權之服務匯流排資源的完整 URI。 例如,http://<namespace>.servicebus.windows.net/<entityPath>sb://<namespace>.servicebus.windows.net/<entityPath>,也就是 http://contoso.servicebus.windows.net/eh1

URI 必須以百分比編碼。

用於簽署的 SAS 規則必須設定於此 URI 或其中一個階層式上層所指定的實體。 例如,先前範例中的 http://contoso.servicebus.windows.net/eh1http://contoso.servicebus.windows.net

SAS 權杖適用於以 signature-string 中所使用的 <resourceURI> 開頭的所有資源。

注意

您可以使用共用存取原則來產生用於事件中樞的存取權杖。 如需詳細資訊,請參閱共用存取授權原則

從原則產生簽章 (權杖)

下一節說明如何使用共用存取簽章原則產生 SAS 權杖。

NodeJS

function createSharedAccessToken(uri, saName, saKey) { 
  if (!uri || !saName || !saKey) { 
          throw "Missing required parameter"; 
      } 
  var encoded = encodeURIComponent(uri); 
  var now = new Date(); 
  var week = 60*60*24*7;
  var ttl = Math.round(now.getTime() / 1000) + week;
  var signature = encoded + '\n' + ttl; 
  var hash = crypto.createHmac('sha256', saKey).update(signature, 'utf8').digest('base64'); 
  return 'SharedAccessSignature sr=' + encoded + '&sig=' +  
      encodeURIComponent(hash) + '&se=' + ttl + '&skn=' + saName; 
}

若要使用原則名稱和索引鍵值來連線到事件中樞,請使用採用 AzureNamedKeyCredential 參數的 EventHubProducerClient 建構函式。

const producer = new EventHubProducerClient("NAMESPACE NAME.servicebus.windows.net", eventHubName, new AzureNamedKeyCredential("POLICYNAME", "KEYVALUE"));

您需要新增 AzureNamedKeyCredential 的參考。

const { AzureNamedKeyCredential } = require("@azure/core-auth");

若要使用透過此程式碼產生的 SAS 權杖,請使用採用 AzureSASCredential 參數的 EventHubProducerClient 建構函式。

var token = createSharedAccessToken("https://NAMESPACENAME.servicebus.windows.net", "POLICYNAME", "KEYVALUE");
const producer = new EventHubProducerClient("NAMESPACENAME.servicebus.windows.net", eventHubName, new AzureSASCredential(token));

您需要新增 AzureSASCredential 的參考。

const { AzureSASCredential } = require("@azure/core-auth");

JAVA

private static String GetSASToken(String resourceUri, String keyName, String key)
  {
      long epoch = System.currentTimeMillis()/1000L;
      int week = 60*60*24*7;
      String expiry = Long.toString(epoch + week);

      String sasToken = null;
      try {
          String stringToSign = URLEncoder.encode(resourceUri, "UTF-8") + "\n" + expiry;
          String signature = getHMAC256(key, stringToSign);
          sasToken = "SharedAccessSignature sr=" + URLEncoder.encode(resourceUri, "UTF-8") +"&sig=" +
                  URLEncoder.encode(signature, "UTF-8") + "&se=" + expiry + "&skn=" + keyName;
      } catch (UnsupportedEncodingException e) {

          e.printStackTrace();
      }

      return sasToken;
  }


public static String getHMAC256(String key, String input) {
    Mac sha256_HMAC = null;
    String hash = null;
    try {
        sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        Encoder encoder = Base64.getEncoder();

        hash = new String(encoder.encode(sha256_HMAC.doFinal(input.getBytes("UTF-8"))));

    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
   } catch (IllegalStateException e) {
        e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    return hash;
}

PHP

function generateSasToken($uri, $sasKeyName, $sasKeyValue) 
{ 
    $targetUri = strtolower(rawurlencode(strtolower($uri))); 
    $expires = time(); 	
    $expiresInMins = 60; 
    $week = 60*60*24*7;
    $expires = $expires + $week; 
    $toSign = $targetUri . "\n" . $expires; 
    $signature = rawurlencode(base64_encode(hash_hmac('sha256', 			
     $toSign, $sasKeyValue, TRUE))); 
    
    $token = "SharedAccessSignature sr=" . $targetUri . "&sig=" . $signature . "&se=" . $expires . 		"&skn=" . $sasKeyName; 
    return $token; 
}

C#

private static string createToken(string resourceUri, string keyName, string key)
{
    TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
    var week = 60 * 60 * 24 * 7;
    var expiry = Convert.ToString((int)sinceEpoch.TotalSeconds + week);
    string stringToSign = HttpUtility.UrlEncode(resourceUri) + "\n" + expiry;
    using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key)))
    {
        var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
        var sasToken = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}", HttpUtility.UrlEncode(resourceUri), HttpUtility.UrlEncode(signature), expiry, keyName);
        return sasToken;
    }
}

PowerShell

[Reflection.Assembly]::LoadWithPartialName("System.Web")| out-null
$URI="myNamespace.servicebus.windows.net/myEventHub/"
$Access_Policy_Name="RootManageSharedAccessKey"
$Access_Policy_Key="myPrimaryKey"
#Token expires now+300
$Expires=([DateTimeOffset]::Now.ToUnixTimeSeconds())+300
$SignatureString=[System.Web.HttpUtility]::UrlEncode($URI)+ "`n" + [string]$Expires
$HMAC = New-Object System.Security.Cryptography.HMACSHA256
$HMAC.key = [Text.Encoding]::ASCII.GetBytes($Access_Policy_Key)
$Signature = $HMAC.ComputeHash([Text.Encoding]::ASCII.GetBytes($SignatureString))
$Signature = [Convert]::ToBase64String($Signature)
$SASToken = "SharedAccessSignature sr=" + [System.Web.HttpUtility]::UrlEncode($URI) + "&sig=" + [System.Web.HttpUtility]::UrlEncode($Signature) + "&se=" + $Expires + "&skn=" + $Access_Policy_Name
$SASToken

BASH

get_sas_token() {
    local EVENTHUB_URI='EVENTHUBURI'
    local SHARED_ACCESS_KEY_NAME='SHAREDACCESSKEYNAME'
    local SHARED_ACCESS_KEY='SHAREDACCESSKEYVALUE'
    local EXPIRY=${EXPIRY:=$((60 * 60 * 24))} # Default token expiry is 1 day

    local ENCODED_URI=$(echo -n $EVENTHUB_URI | jq -s -R -r @uri)
    local TTL=$(($(date +%s) + $EXPIRY))
    local UTF8_SIGNATURE=$(printf "%s\n%s" $ENCODED_URI $TTL | iconv -t utf8)

    local HASH=$(echo -n "$UTF8_SIGNATURE" | openssl sha256 -hmac $SHARED_ACCESS_KEY -binary | base64)
    local ENCODED_HASH=$(echo -n $HASH | jq -s -R -r @uri)

    echo -n "SharedAccessSignature sr=$ENCODED_URI&sig=$ENCODED_HASH&se=$TTL&skn=$SHARED_ACCESS_KEY_NAME"
}

使用 SAS 驗證事件中樞發行者

事件發行者會定義事件中樞的虛擬端點。 發行者只能用來將訊息傳送至事件中樞,不能用來接收訊息。

一般而言,事件中樞會為每個用戶端採用一個發行者。 所有傳送至事件中樞任一發行者的訊息都會加入該事件中樞內的佇列。 發行者可啟用細部的存取控制。

系統會為每個「事件中樞」用戶端指派一個唯一權杖,該權杖會上傳到用戶端。 權杖的產生機制讓每個唯一權杖都能授與相異唯一發佈者的存取權限。 保存權杖的用戶端只能傳送至一個發行者,而不能傳送至其他發行者。 如果多個用戶端共用同一個權杖,則每個用戶端都會共用發行者。

所有權杖都是使用 SAS 金鑰來指派。 一般而言,所有權杖都會使用相同的金鑰簽署。 用戶端並不知道金鑰,這可防止用戶端製造權杖。 用戶端會使用相同的權杖進行操作,直到權杖到期為止。

例如,若要定義範圍僅限於傳送/發佈至事件中樞的授權規則,您必須定義傳送授權規則。 其可以在命名空間層級進行,或為特定實體 (事件中樞執行個體或主題) 提供更細微的範圍。 具有這類細微存取範圍的用戶端或應用程式稱為事件中樞發行者。 若要如此做,請執行下列步驟:

  1. 在您想要發佈的實體上建立 SAS 金鑰,以指派其上的傳送範圍。 如需詳細資訊,請參閱共用存取授權原則

  2. 使用步驟 1 中產生的權杖,針對特定發行者產生具有到期時間的 SAS 權杖。 如需範例程式碼,請參閱從原則產生簽章 (權杖)

  3. 將權杖提供給發行者用戶端,其只能傳送至權杖授與存取權的實體和發行者。

    一旦權杖到期,用戶端就會失去其傳送/發行至實體的存取權。

注意

您可以讓裝置具備可授與事件中樞或命名空間存取權的權杖,不過我們不建議這樣做。 任何擁有此權杖的裝置都可以將訊息直接傳送到該事件中樞。 此外,您也無法將這類裝置加入封鎖清單,以禁止其傳送到該事件中樞。

我們建議您提供特定且細微的範圍。

重要

建立權杖之後,系統就會為每個用戶端佈建其自己的唯一權杖。

當用戶端將資料傳送到事件中樞時,會使用權杖標記自己的要求。 為了防止攻擊者竊聽及竊取權杖,用戶端與事件中樞之間的通訊必須透過已加密的通道進行。

如果權杖遭攻擊者竊取,攻擊者便可以模擬權杖遭竊的用戶端。 將發行者列入封鎖清單可讓用戶端變成無法使用,直到它收到使用不同發行者的新權杖為止。

使用 SAS 驗證事件中樞取用者

若要對取用事件中樞產生者所產生資料的後端應用程式進行驗證,事件中樞權杖驗證會要求其用戶端將管理權限或接聽權限指派給其事件中樞命名空間或是事件中樞執行個體或主題。 系統會使用取用者群組從事件中樞取用資料。 雖然 SAS 原則提供了細微的範圍,但此範圍只能在實體層級定義,而不是在取用者層級定義。 這表示在命名空間層級或事件中樞或主題層級所定義的權限,將套用至該實體的取用者群組。

停用本機/SAS 金鑰驗證

針對某些組織安全性需求,您可能必須完全停用本機/SAS 金鑰驗證,並且依賴 Microsoft Entra ID 型驗證,這是與 Azure 事件中樞連接的建議方式。 您可以使用 Azure 入口網站或 Azure Resource Manager 範本,在事件中樞命名空間層級停用本機/SAS 金鑰驗證。

透過此入口網站停用本機/SAS 金鑰驗證

您可以使用 Azure 入口網站來停用指定事件中樞命名空間的本機/SAS 金鑰驗證。

  1. 在 Azure 入口網站中,瀏覽到您的事件中樞命名空間。

  2. 如下圖所示,在 [概觀] 頁面的 [本機驗證] 選取 [啟用]

    螢幕擷取畫面,其中顯示已選取 [本機驗證]。

  3. 在 [本機驗證] 快顯視窗,選取 [已停用],再選取 [確定]

    [本機驗證] 快顯視窗的螢幕擷取畫面,其中顯示已選取 [已停用] 選項。

使用範本停用本機/SAS 金鑰驗證

您可以透過將 disableLocalAuth 屬性設定為 true,以停用指定事件中樞命名空間的本機驗證,如下列 Azure Resource Manager 範本 (ARM 範本) 所示。

"resources":[
      {
         "apiVersion":"[variables('ehVersion')]",
         "name":"[parameters('eventHubNamespaceName')]",
         "type":"Microsoft.EventHub/Namespaces",
         "location":"[variables('location')]",
         "sku":{
            "name":"Standard",
            "tier":"Standard"
         },
         "resources": [
    {
      "apiVersion": "2017-04-01",
      "name": "[parameters('eventHubNamespaceName')]",
      "type": "Microsoft.EventHub/Namespaces",
      "location": "[resourceGroup().location]",
      "sku": {
        "name": "Standard"
      },
      "properties": {
        "isAutoInflateEnabled": "true",
        "maximumThroughputUnits": "7", 
        "disableLocalAuth": true
      },
      "resources": [
        {
          "apiVersion": "2017-04-01",
          "name": "[parameters('eventHubName')]",
          "type": "EventHubs",
          "dependsOn": [
            "[concat('Microsoft.EventHub/namespaces/', parameters('eventHubNamespaceName'))]"
          ],
          "properties": {
            "messageRetentionInDays": "[parameters('messageRetentionInDays')]",
            "partitionCount": "[parameters('partitionCount')]"
          }

        }
      ]
    }
  ]

範例

  • 請參閱此 GitHub 位置中的 .NET 範例 #6,瞭解如何使用共用存取認證或預設的 Azure 認證身分識別,將事件發佈至事件中樞。
  • 請參閱此 GitHub 位置中的 .NET 範例 #5,瞭解如何使用共用存取認證或預設 Azure 認證身分識別來取用或處理事件。

下一步

請參閱以下文章:

請參閱下列相關文章: