在 Azure AI 搜尋服務中設定客戶自控金鑰以進行資料加密

Azure AI 搜尋服務會使用服務管理的金鑰,來自動將待用資料加密。 如果需要更多保護,您可以使用在 Azure Key Vault 中所建立及管理的金鑰,以其他的加密層來增補預設的加密。

本文章將逐步引導您設定「客戶自控金鑰」(CMK) 或「攜帶您自己的金鑰」(BYOK) 加密的步驟。

注意

如果索引已經過 CMK 加密,則只有在搜尋服務有權存取該金鑰時,才能存取該索引。 如果撤銷存取權,索引將無法使用,而且在刪除索引或還原密鑰的存取權之前,無法調整服務。

CMK 加密的物件

CMK 加密是在個別的物件上制定的。 如果您在整個搜尋服務中需要 CMK,請設定強制原則

建立物件後,CMK 加密即可運作。 您無法對已存在的物件進行加密。 每當物件儲存到磁碟時 (無論是長期儲存的待用資料,還是短期儲存的暫存資料),就會發生 CMK 加密。 使用 CMK 時,磁碟永遠不會看到未加密的資料。

可加密的物件包括索引、同義字清單、索引子、資料來源和技能集。 在計算上,加密後再解密的成本會很高,因此只會對機密性的內容進行加密。

加密會透過下列內容來執行:

  • 索引和同義字清單內的所有內容。

  • 索引器、數據源、技能集和向量化工具中的敏感性內容。 此內容只包含儲存 連接字串、描述、身分識別、金鑰和使用者輸入的欄位。 例如,技能集具有 Azure AI 服務金鑰,而有些技能接受使用者輸入,例如自訂實體。 在這兩種情況下,都會針對金鑰和對技能的使用者輸入進行加密。 外部資源的任何參考 (例如 Azure 資料來源或 Azure OpenAI 模型) 也會加密。

完整雙重加密

當您引進 CMK 加密時,您會對內容進行兩次加密。 針對上一節所述的物件和欄位,內容會先使用您的 CMK 進行加密,然後使用 Microsoft 管理的金鑰進行加密。 在長期儲存的資料磁碟和短期儲存的暫存磁碟上的內容會進行雙重加密。

啟用 CMK 加密會增加索引大小,並降低查詢效能。 根據迄今為止的觀察,查詢時間大約會增加 30%-60%,不過實際效能會取決於索引定義和查詢類型。 因為效能會降低,因此建議您只在真正需要此功能的對象上啟用此功能。

雖然所有地區現在都有雙重加密可用,但支援會在兩個階段推出:

  • 第一次推出是在 2020 年 8 月 1 日,並包含下列五個地區。 在下列區域中建立的搜尋服務支援資料磁碟的 CMK,但不支援暫存磁碟:

    • 美國西部 2
    • 美國東部
    • 美國中南部
    • US Gov 維吉尼亞州
    • US Gov 亞利桑那州
  • 2021 年 5 月 13 日的第二次推出新增了暫存磁碟的加密,並將 CMK 加密延伸至所有支援的區域

    如果您是從第一次推出期間建立的服務使用 CMK,而且也希望暫存磁碟進行 CMK 加密,則必須在您選擇的區域中建立新的搜尋服務,並重新部署內容。 若要判斷您的服務建立日期,請參閱 如何檢查服務建立日期

必要條件

限制

  • 不支援 Azure 金鑰保存庫 受控硬體安全性模型 (HSM)。

  • 不支援在 Azure 入口網站 中新增加密金鑰。

  • 不支援跨訂用帳戶。 Azure 金鑰保存庫 和 Azure AI 搜尋必須位於相同的訂用帳戶中。

金鑰保存庫秘訣

如果您不熟悉 Azure Key Vault,請檢閱本快速入門以了解基本工作:使用 PowerShell 從 Azure Key Vault 設定及擷取秘密

以下是使用 Key Vault 的一些秘訣:

  • 依所需使用多個金鑰保存庫。 受控金鑰可以位於不同的金鑰保存庫中。 搜尋服務可以有多個加密的物件,每個物件都以不同的客戶管理的加密金鑰 (儲存在不同的金鑰保存庫中) 來加密。

  • 使用相同的租使用者,讓您可以透過系統或使用者管理的身分識別連線來擷取受控密鑰。 此行為要求這兩個服務共用相同的租用戶。 如需建立租用戶的詳細資訊,請參閱 設定新的租使用者

  • 啟用清除保護和虛刪除。 由於使用客戶管理的金鑰進行加密的性質,如果您的 Azure Key Vault 金鑰遭到刪除,就沒有人可以擷取您的資料。 為了防止因意外刪除 Key Vault 金鑰而導致資料遺失,必須在金鑰保存庫上啟用虛刪除和清除保護。 默認會啟用虛刪除,因此只有在您故意停用時,您才會遇到問題。 清除保護依預設不會啟用,但在 Azure AI 搜尋服務中,客戶自控金鑰加密需要它。

  • 啟用金鑰保存庫的記錄 ,以便監視金鑰使用方式。

  • 在金鑰保存庫金鑰和應用程式秘密和註冊的例行輪替期間,啟用自動輪 替密鑰或遵循嚴格的程式。 在刪除舊的秘密和金鑰之前,始終先更新所有加密的內容才來使用新的秘密和金鑰。 如果您錯過此步驟,就無法對您的內容進行解密。

步驟 1:在 金鑰保存庫 中建立金鑰

如果在 Azure Key Vault 中您已有想要使用的金鑰時,請跳過金鑰產生作業,但收集金鑰識別碼。 建立加密物件時,您將需要此資訊。

新增金鑰之前,請確定您已將金鑰保存庫密碼編譯人員角色指派給自己。

Azure AI 搜尋加密支援大小為 2048、3072 和 4096 的 RSA 金鑰。 若要進一步了解支援的金鑰類型,請參閱關於金鑰

  1. 登入 Azure 入口網站並開啟您的金鑰保存庫概觀頁面。

  2. 選取左側的 [物件>索引鍵],然後選取 [產生/匯入]。

  3. 在 [建立金鑰] 窗格中,從 [選項] 清單中,選擇 [產生] 以建立新的金鑰。

  4. 輸入金鑰的 [ 名稱 ],並接受其他金鑰屬性的預設值。

  5. 或者,設定金鑰輪替原則以 啟用自動輪替

  6. 選取 [建立] 以開始部署。

  7. 選取金鑰、選取目前的版本,然後記下金鑰識別碼。 其是由金鑰值 Uri金鑰名稱金鑰版本所組成。 您需要此識別碼,才能在 Azure AI 搜尋服務中定義加密的索引。

    建立新的 Key Vault 金鑰

步驟 2:建立安全性主體

您有數個選項可用來設定 Azure AI 搜尋服務在執行時間存取加密金鑰。 最簡單的方法是使用搜尋服務的受控識別來擷取密鑰。 您可以使用系統或使用者受控的識別。 這樣做可讓您省略應用程式註冊和應用程式秘密的步驟。 或者,您可以建立並註冊Microsoft Entra 應用程式,並讓搜尋服務在要求上提供應用程式識別碼。

我們建議使用受控識別。 受控識別可讓您的搜尋服務向 Azure Key Vault 進行驗證,而不需將認證 (ApplicationID 或 ApplicationSecret) 儲存在程式碼中。 這種類型的受控識別生命週期會系結至您的搜尋服務的生命週期,此生命週期只能有一個系統指派的受控識別。 如需受控識別如何運作的詳細資訊,請參閱什麼是 Azure 資源的受控識別

為您的搜尋服務啟用系統指派的受控識別。

開啟系統指派受控識別的螢幕快照。

步驟 3:授與許可權

Azure 金鑰保存庫 支援使用角色型訪問控制進行授權。 我們建議透過金鑰保存庫存取原則來使用此方法。 如需詳細資訊,請參閱使用 Azure 角色提供 金鑰保存庫 金鑰、憑證和秘密的存取權。

在此步驟中,將 金鑰保存庫 加密服務加密使用者角色指派給您的搜尋服務。 如果您要在本機測試,也請將此角色指派給自己。

  1. 登入 Azure 入口網站 並尋找您的金鑰保存庫。

  2. 選取 [訪問控制][IAM], 然後選取 [ 新增角色指派]。

  3. 選取 [金鑰保存庫 加密服務加密使用者],然後選取 [下一步]。

  4. 選取受控識別、選取成員,然後選取搜尋服務的受控識別。

  5. 選取 [檢閱 + 指派]

等候幾分鐘,讓角色指派變成可運作。

步驟 4:加密內容

當您建立物件時,會新增加密金鑰。 若要對索引、同義字對應、索引子、資料來源或技能集新增客戶自控金鑰,請使用 Search REST API 或 Azure SDK 來建立已啟用加密的物件。 若要使用 Azure SDK 新增加密,請參閱 本文中的 Python 範例

  1. 呼叫建立 API 以指定 encryptionKey 屬性:

  2. 將 encryptionKey 建構插入物件定義中。 此屬性是第一層屬性,其層級與名稱和描述相同。 如果您使用相同的保存庫、金鑰和版本,您可以將相同的 encryptionKey 建構貼到每個物件定義中。

    第一個範例顯示使用受控識別連線之搜尋服務的 encryptionKey:

    {
      "encryptionKey": {
        "keyVaultUri": "<YOUR-KEY-VAULT-URI>",
        "keyVaultKeyName": "<YOUR-ENCRYPTION-KEY-NAME>",
        "keyVaultKeyVersion": "<YOUR-ENCRYPTION-KEY-VERSION>"
      }
    }
    

    第二個範例包含 accessCredentials,如果您在 entra ID Microsoft註冊應用程式,則為必要專案:

    {
      "encryptionKey": {
        "keyVaultUri": "<YOUR-KEY-VAULT-URI>",
        "keyVaultKeyName": "<YOUR-ENCRYPTION-KEY-NAME>",
        "keyVaultKeyVersion": "<YOUR-ENCRYPTION-KEY-VERSION>",
        "accessCredentials": {
          "applicationId": "<YOUR-APPLICATION-ID>",
          "applicationSecret": "<YOUR-APPLICATION-SECRET>"
        }
      }
    }
    
  3. 藉由在 對象上發出 GET 來確認加密金鑰是否存在。

  4. 執行工作來確認物件是否正常運作,例如查詢已加密的索引。

一旦您在搜尋服務上建立了加密的物件之後,就可以像使用任何其他同類型的物件一樣使用它。 加密對使用者和開發人員而言都是透明的。

這些金鑰保存庫詳細資料都不會被視為秘密,而且可以透過瀏覽至 Azure 入口網站中的相關 Azure Key Vault 頁面來輕鬆擷取。

重要

Azure AI 搜尋服務中的加密內容已設定為使用含特定版本的特定 Azure Key Vault 金鑰。 如果您變更金鑰或版本,必須先更新 物件,才能刪除前一個。 無法這麼做會轉譯無法使用的物件。 如果金鑰遺失了,您將無法對內容進行解密。

步驟 5:測試加密

若要確認加密是否正常運作,請撤銷加密金鑰、查詢索引(應該無法使用),然後恢復加密密鑰。

針對此工作使用 Azure 入口網站。

  1. 在 [Azure 金鑰保存庫] 頁面上,選取 [物件>密鑰]。

  2. 選取您剛才建立的金鑰,然後選取 [ 刪除]。

  3. 在 [Azure AI 搜尋] 頁面上,選取 [搜尋管理>索引]。

  4. 選取您的索引,並使用 [搜尋總管] 來執行查詢。 您應該會收到錯誤。

  5. 返回 [Azure 金鑰保存庫 物件>金鑰] 頁面。

  6. 選取 [ 管理已刪除的金鑰]。

  7. 選取您的金鑰,然後選取 [ 復原]。

  8. 返回 Azure AI 搜尋服務中的索引,然後重新執行查詢。 您應該會看到搜尋結果。 如果您沒有看到立即的結果,請稍候一分鐘再試一次。

設定原則以強制執行 CMK 合規性

Azure 原則有助於強制執行組織標準及大規模評估合規性。 Azure AI 搜尋服務具有選擇性的內建原則 (適用於服務範圍 CMK 強制執行)

在本節中,您會設定原則來定義搜尋服務的 CMK 標準。 然後,設定搜尋服務以強制執行此原則。

  1. 在網頁瀏覽器中瀏覽至 內建原則。 選取 [指派]

    指派內建 CMK 原則的螢幕擷取畫面。

  2. 設定原則範圍。 在 [參數] 區段中,取消核取 [僅顯示參數...],並將 [效果] 設定為 [拒絕]

    在評估要求期間,符合拒絕原則定義的要求會標示為不符合規範。 假設服務的標準是 CMK 加密,「拒絕」表示未指定 CMK 加密的要求不符合規範。

    將內建 CMK 原則效果變更為拒絕的螢幕擷取畫面。

  3. 完成建立原則。

  4. 呼叫服務 - 更新 API,以在服務層級啟用 CMK 原則強制執行。

PATCH https://management.azure.com/subscriptions/<your-subscription-Id>/resourceGroups/<your-resource-group-name>/providers/Microsoft.Search/searchServices/<your-search-service-name>?api-version=2023-11-01

{
    "properties": {
        "encryptionWithCmk": {
            "enforcement": "Enabled"
        }
    }
}

輪替或更新加密金鑰

建議您使用 Azure 金鑰保存庫 的自動輪替功能,但您也可以手動輪替密鑰。

當您變更索引鍵或其版本時,使用密鑰的任何對象必須先更新為使用新值,才能刪除舊值。 否則,物件會變成無法使用,因為它無法解密。

  1. 確定索引或同義字對應所使用的金鑰

  2. 在金鑰保存庫中建立新的金鑰,但讓原始金鑰保持可用的狀態。

  3. 對索引或同義字對應更新 encryptionKey 屬性,以使用新的值。 只有最初使用此屬性所建立的物件可以進行更新以使用不同的值。

  4. 停用或刪除金鑰保存庫中之前的金鑰。 監視金鑰存取,以確認是否正在使用新的金鑰。

基於效能的理由,搜尋服務最多會快取金鑰數小時。 如果您停用或刪除金鑰而不提供新的金鑰,查詢還是會暫時繼續運作,直到快取時間到期為止。 不過,一旦搜尋服務無法再對內容進行解密,您就會收到下列訊息:"Access forbidden. The query key used might have been revoked - please retry."

處理加密的內容

使用客戶自控金鑰加密時,由於額外的加密/解密工作,您會察覺到編製索引和查詢的延遲問題。 Azure AI 搜尋服務不會記錄加密活動,但您可以透過金鑰保存庫記錄來監視金鑰存取。

建議您啟用記錄作為金鑰保存庫組態的一部分。

  1. 建立 Log Analytics 工作區

  2. 在金鑰保存庫中 新增診斷設定,以使用工作區來保留數據。

  3. 選取 類別的 auditallLogs 、提供診斷設定名稱,然後儲存它。

加密金鑰組態的 Python 範例

本節顯示 物件定義中 的 Python 表示 encryptionKey 法。 相同的定義適用於索引、數據源、技能組、索引器和同義字對應。 若要在搜尋服務和密鑰保存庫中試用此範例,請從 azure-search-python-samples 下載筆記本。

安裝一些套件。

! pip install python-dotenv
! pip install azure-core
! pip install azure-search-documents==11.5.1
! pip install azure-identity

建立具有加密金鑰的索引。

from azure.search.documents.indexes import SearchIndexClient
from azure.search.documents.indexes.models import (
    SimpleField,
    SearchFieldDataType,
    SearchableField,
    SearchIndex,
    SearchResourceEncryptionKey
)
from azure.identity import DefaultAzureCredential

endpoint="<PUT YOUR AZURE SEARCH SERVICE ENDPOINT HERE>"
credential = DefaultAzureCredential()

index_name = "test-cmk-index"
index_client = SearchIndexClient(endpoint=endpoint, credential=credential)  
fields = [
        SimpleField(name="Id", type=SearchFieldDataType.String, key=True),
        SearchableField(name="Description", type=SearchFieldDataType.String)
    ]

scoring_profiles = []
suggester = []
encryption_key = SearchResourceEncryptionKey(
    key_name="<PUT YOUR KEY VAULT NAME HERE>",
    key_version="<PUT YOUR ALPHANUMERIC KEY VERSION HERE>",
    vault_uri="<PUT YOUR KEY VAULT ENDPOINT HERE>"
)

index = SearchIndex(name=index_name, fields=fields, encryption_key=encryption_key)
result = index_client.create_or_update_index(index)
print(f' {result.name} created')

取得索引定義,以確認加密金鑰組態存在。

index_name = "test-cmk-index-qs"
index_client = SearchIndexClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)  

result = index_client.get_index(index_name)  
print(f"{result}")  

使用一些檔載入索引。 所有欄位內容都會被視為敏感性,並使用客戶管理的金鑰在磁碟上加密。

from azure.search.documents import SearchClient

# Create a documents payload
documents = [
    {
    "@search.action": "upload",
    "Id": "1",
    "Description": "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities."
    },
    {
    "@search.action": "upload",
    "Id": "2",
    "Description": "The hotel is situated in a  nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts."
    },
    {
    "@search.action": "upload",
    "Id": "3",
    "Description": "The hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel's restaurant services."
    },
    {
    "@search.action": "upload",
    "Id": "4",
    "Description": "The hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Cliff is part of a lovingly restored 1800 palace."
    }
]

search_client = SearchClient(endpoint=AZURE_SEARCH_SERVICE, index_name=index_name, credential=credential)
try:
    result = search_client.upload_documents(documents=documents)
    print("Upload of new document succeeded: {}".format(result[0].succeeded))
except Exception as ex:
    print (ex.message)

    index_client = SearchClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)

執行查詢以確認索引可運作。

from azure.search.documents import SearchClient

query = "historic"  

search_client = SearchClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential, index_name=index_name)
  
results = search_client.search(  
    query_type='simple',
    search_text=query, 
    select=["Id", "Description"],
    include_total_count=True
    )
  
for result in results:  
    print(f"Score: {result['@search.score']}")
    print(f"Id: {result['Id']}")
    print(f"Description: {result['Description']}")

查詢的輸出應該會產生類似下列範例的結果。

Score: 0.6130029
Id: 4
Description: The hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Cliff is part of a lovingly restored 1800 palace.
Score: 0.26286605
Id: 1
Description: The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.

由於加密的內容會在數據重新整理或查詢之前解密,因此您不會看到加密的視覺辨識項。 若要確認加密是否正常運作,請檢查資源記錄。

下一步

如果您不熟悉 Azure 安全性架構,請檢閱 Azure 安全性文件,特別是下面這篇文章: