次の方法で共有


Azure AI Search の結果をトリミングするためのセキュリティ フィルター

Azure AI 検索はネイティブのドキュメントレベル アクセス許可を提供していないため、ユーザーのアクセス許可によって同じインデックス内からの検索結果を変更することができません。 回避策として、グループまたはユーザー ID を含む文字列に基づいて検索結果をトリミングするフィルターを作成できます。

この記事では、以下の手順を含むセキュリティ フィルター処理のパターンについて説明します。

  • 必要なコンテンツが含まれたソース ドキュメントを作成する
  • プリンシパル識別子のフィールドを作成する
  • インデックス作成のためにドキュメントを検索インデックスにプッシュする
  • search.in フィルター関数を使用してインデックスに対するクエリを実行する

最後に、ハンズオンの学習を提供するデモと例へのリンクを示します。 パターンを理解するには、まずこの記事を確認することをお勧めします。

セキュリティ フィルター パターンについて

Azure AI 検索は、インデックス内のコンテンツにアクセスするためのセキュリティ サブシステムと統合されていませんが、ドキュメントレベルのセキュリティ要件を持つお客様の多くが、フィルターによって自分のニーズを満たせると考えています。

Azure AI 検索において、セキュリティ フィルターは、セキュリティ プリンシパルで構成される文字列に基づいて検索結果を含めたり除外したりする通常の OData フィルターです。 セキュリティ プリンシパルによる認証や認可はありません。 プリンシパルは単なる文字列であり、検索結果にドキュメントを含めたり除外したりするためにフィルター式で使用されます。

セキュリティ フィルター処理を実現するには、いくつかの方法があります。 その 1 つは、Id eq 'id1' or Id eq 'id2' など、等値式の複雑な論理和演算を使用する方法です。 この方法は誤りが発生しやすく、保守が困難です。また、一覧の値の数が数百、数千単位の場合、クエリの応答時間が何秒も遅くなります。

この記事で説明するように、推奨される解決策として、セキュリティ フィルターに search.in 関数を使用します。 等値式ではなく search.in(Id, 'id1, id2, ...') を使用すると、1 秒未満の応答時間を期待できます。

前提条件

  • Microsoft Entra オブジェクトの識別子など、グループまたはユーザー ID を含む文字列フィールド。

  • 同じドキュメント内の他のフィールドは、そのグループまたはユーザーがアクセスできるコンテンツを提供する必要があります。 以下の JSON ドキュメントでは、"security_id" フィールドにセキュリティ フィルターで使用される ID が含まれており、呼び出し元の ID がドキュメントの "security_id" と一致する場合、名前、給与、結婚状況が含められます。

    {  
        "Employee-1": {  
            "employee_id": "100-1000-10-1-10000-1",
            "name": "Abram",   
            "salary": 75000,   
            "married": true,
            "security_id": "alphanumeric-object-id-for-employee-1"
        },
        "Employee-2": {  
            "employee_id": "200-2000-20-2-20000-2",
            "name": "Adams",   
            "salary": 75000,   
            "married": true,
            "security_id": "alphanumeric-object-id-for-employee-2"
        } 
    }  
    

セキュリティ フィールドを作成する

検索インデックスでは、フィールド コレクション内に、前の例の架空の "security_id" フィールドと同様の、グループまたはユーザー ID を含む 1 つのフィールドが必要です。

  1. セキュリティ フィールドを Collection(Edm.String) として追加します。

  2. フィールドの filterable 属性を true に設定します。

  3. 検索要求の一部として返されないように、フィールドの retrievable 属性を false に設定します。

  4. インデックスにはドキュメント キーが必要です。 "file_id" フィールドがこの要件を満たしています。

  5. インデックスには、検索可能かつ取得可能なコンテンツを含める必要もあります。 この例では、"file_name" と "file_description" のフィールドがそれを表しています。

    次のインデックス スキーマは、フィールドの要件を満たしています。 Azure AI 検索でインデックスを付けるドキュメントは、"group_ids" を含め、これらのフィールドすべての値を持っている必要があります。 たとえば、file_name が "secured_file_b" であるドキュメントの場合、そのファイルへの読み取りアクセス権を持つのはグループ ID "group_id1" または "group_id2" に属するユーザーだけです。

    POST https://[search service].search.windows.net/indexes/securedfiles/docs/index?api-version=2024-07-01
    {
         "name": "securedfiles",  
         "fields": [
             {"name": "file_id", "type": "Edm.String", "key": true, "searchable": false },
             {"name": "file_name", "type": "Edm.String", "searchable": true },
             {"name": "file_description", "type": "Edm.String", "searchable": true },
             {"name": "group_ids", "type": "Collection(Edm.String)", "filterable": true, "retrievable": false }
         ]
     }
    

REST API を使用してインデックスにデータをプッシュする

セキュリティ フィールドの値を含め、フィールド コレクション内の各フィールドの値を提供するドキュメントを検索インデックスに設定します。 Azure AI 検索には、セキュリティ フィールドを設定するための特別な API や機能は用意されていません。 しかし、この記事の最後に示すいくつかの例では、このフィールドを設定する手法について説明します。

Azure AI 検索においてデータを読み込む方法は以下のとおりです。

  • すべてのフィールドが設定されたドキュメントをインポートする 1 回のプッシュまたはプル (インデクサー) 操作
  • 複数のプッシュまたはプル操作。 二次的なインポート操作が適切なドキュメント識別子を対象としている限り、複数のインポートを通してフィールドを個別に読み込むことができます。

次の例は、インデックスの URL エンドポイントのドキュメント コレクションに対する 1 つの HTTP POST 要求を示しています (「ドキュメント - インデックス」を参照してください)。 HTTP 要求の本文は、インデックスを作成するドキュメントの JSON レンダリングです。

POST https://[search service].search.windows.net/indexes/securedfiles/docs/index?api-version=2024-07-01
{
    "value": [
        {
            "@search.action": "upload",
            "file_id": "1",
            "file_name": "secured_file_a",
            "file_description": "File access is restricted to Human Resources.",
            "group_ids": ["group_id1"]
        },
        {
            "@search.action": "upload",
            "file_id": "2",
            "file_name": "secured_file_b",
            "file_description": "File access is restricted to Human Resources and Recruiting.",
            "group_ids": ["group_id1", "group_id2"]
        },
        {
            "@search.action": "upload",
            "file_id": "3",
            "file_name": "secured_file_c",
            "file_description": "File access is restricted to Operations and Logistics.",
            "group_ids": ["group_id5", "group_id6"]
        }
    ]
}

グループの一覧を使用して既存のドキュメントを更新する必要がある場合は、merge または mergeOrUpload アクションを使用できます。

{
    "value": [
        {
            "@search.action": "mergeOrUpload",
            "file_id": "3",
            "group_ids": ["group_id7", "group_id8", "group_id9"]
        }
    ]
}

クエリでセキュリティ フィルターを適用する

group_ids のアクセス権に基づいてドキュメントをトリミングするには、group_ids/any(g:search.in(g, 'group_id1, group_id2,...')) フィルターを含む検索クエリを発行する必要があります (この "group_id1, group_id2,..." は、検索要求の発行元が属するグループです)。

このフィルターは、指定された識別子のいずれかが group_ids フィールドに含まれるすべてのドキュメントと一致します。 Azure AI Search を使用してドキュメントを検索する方法の詳細については、ドキュメントの検索に関するページを参照してください。

このサンプルでは、POST 要求を使用してクエリを設定する方法を示します。

以下のような HTTP POST 要求を発行し、要求本文でフィルターを指定します。

POST https://[service name].search.windows.net/indexes/securedfiles/docs/search?api-version=2024-07-01

{
   "filter":"group_ids/any(g:search.in(g, 'group_id1, group_id2'))"  
}

group_ids に "group_id1" または "group_id2" が含まれるドキュメントが返されます。 つまり、要求の発行元が読み取りアクセス権を持っているドキュメントが取得されます。

{
 [
   {
    "@search.score":1.0,
     "file_id":"1",
     "file_name":"secured_file_a",
   },
   {
     "@search.score":1.0,
     "file_id":"2",
     "file_name":"secured_file_b"
   }
 ]
}

次のステップ

この記事では、ユーザー ID と search.in() 関数に基づいて結果をフィルター処理するためのパターンについて説明します。 この関数を使用して、各ターゲット ドキュメントに関連付けられたプリンシパルの識別子と一致する要求元のユーザーについて、プリンシパルの識別子を渡すことができます。 検索要求が処理されると、search.in 関数によって、ユーザーのプリンシパルが読み取りアクセス権を持っていない検索結果は除外されます。 プリンシパルの識別子は、セキュリティ グループ、ロール、さらにはユーザー自身の ID を表す場合があります。

その他の例、デモ、ビデオについては以下を参照してください。