Pipeline オペレーションのセキュリティ ルール

パイプライン オペレーション ルールを使用すると、既存のルールエンジンのフィルタリング機能に一致させることができます。パイプライン オペレーションは豊富な機能を提供しますが、ルールエンジンはクエリの充足性とセキュリティを確保するために、単純なフィルタのみ認識するように制限されています。

エディションとモードの要件

このドキュメントは、ネイティブ モードの Firestore Enterprise エディションに適用されます。

サポートされているフィルタ式

クエリをルールで制約するには、定数に対して標準の比較演算子を使用する必要があります。ルールエンジンでは、次のフィルタタイプが認識されます。

  • 等式と不等式: eq、neq
  • 比較: gt、gte、lt、lte
  • メンバーシップ: in、arrayContains

次に例を示します。

  • where(eq("foo", 2))
  • where(lt("foo", 2))
  • documents("/user/1", "/user/2").where(...)

リクエストのプロパティ

引き続き request オブジェクトを使用して認証とクエリ コンテキストを検証することはできますが、標準クエリで使用できるプロパティの中には、パイプラインでサポートされていないものもあります。

サポートされているプロパティ

新しいエンジンは引き続き次のプロパティをサポートしています。

  • request.auth: ユーザー uid とトークンデータにアクセスします。
  • request.method: オペレーションを識別します(get、list など)。
  • request.path: アクセスされているリソースのパス。
  • request.time: リクエストのサーバーサイドのタイムスタンプ。

サポートされていないプロパティ

limit、offset、orderBy などの request.query プロパティは、マルチステージ クエリでこれらの値を特定する複雑さのため、パイプライン ルールチェックではサポートされていません。

パイプライン ステージの処理と権限

セキュリティ ルールの特定の細かなオペレーションにマッピングされるパイプライン ステージは次のとおりです。

  • allow list 権限: collection()、collectionGroup()、database() の各ステージでトリガーされます。
  • allow get 権限: documents() ステージによってトリガーされ、get バッチ オペレーションと同様に扱われます。
  • allow create 権限: insert() ステージと、新しいドキュメントを作成するときの upsert() ステージでトリガーされます。
  • allow update 権限: update() ステージと、既存のドキュメントを更新する際の upsert() ステージでトリガーされます。
  • allow delete 権限: delete() ステージでトリガーされます。
  • allow write 権限: すべての DML オペレーション(insert()、update()、upsert()、delete())に適用されます。
  • フィールド変更ステージ: ルールは、派生値ではなく、保存データに対してのみ動作します。パイプラインにフィールドを変更するステージ(AddFields、ReplaceWith、Select など)が含まれている場合、そのステージが検出されると、ルールエンジンはフィルタ制約の適用を停止します。
  • リテラル ステージ: literals() ステージはデータベースから読み取りませんが、費用が発生する可能性があります。不正使用を防ぐため、ルールで検証できる別のステージ(collection() など)とペアにする必要があります。

データ操作言語(DML)ステージとセキュリティ ルール

データ操作言語(DML)ステージ(insert()、update()、upsert()、delete())を実行する場合、セキュリティ ルールは読み取りクエリとは異なる方法で権限を評価します。

  • ドキュメントごとのルール評価: 読み取りパイプラインでは、計画時にクエリを制限するためにフィルタ式を事前に指定する必要がありますが、DML オペレーションでは、個々のドキュメントのミューテーションに対してセキュリティ ルールが評価されます。
  • ルール変数:
    • request.resource: insert()、update()、upsert() オペレーション用に提案されたドキュメント データが含まれます。
    • resource: 変更前にデータベースに保存されている現在のドキュメント データが含まれます(既存のドキュメントの update()、delete()、upsert() で使用可能)。

DML のシステム上限と実行動作

セキュリティ ルールを使用して DML オペレーションを実行する場合は、次のシステム制限と実行動作に注意してください。

get() と exists() の 20 回のルックアップ制限

Firebase セキュリティ ルールでは、リクエスト全体で最大 20 件のドキュメント ルックアップ(get() または exists())が可能です。

DML パイプラインが明示的なトランザクションの外部で実行されると、ドキュメントはバッチで処理されます。各バッチは独自のトランザクションで実行され、ドキュメントはバッチ間で同時に変更される可能性があるため、セキュリティ ルールとドキュメントのルックアップは書き込みバッチごとに再評価されます。

セキュリティ ルールが単一の静的ドキュメントに対して評価される場合でも(ユーザーが管理者かどうかを確認する場合など):

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /posts/{postId} {
      allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.isAdmin == true;
    }
  }
}

ドキュメントの書き込みバッチごとにルックアップが実行されます。その結果、可能なすべてのドキュメントを書き込んだ後、リクエストの 20 回のルックアップ上限に達し、パイプラインは PERMISSION_DENIED エラーで失敗します。また、書き込みバッチごとに実行されるルックアップごとに、独自の読み取り課金が発生します。

一部成功と PERMISSION_DENIED

トランザクションの外部で実行する場合、DML オペレーションは書き込みを順次バッチで commit します。ドキュメントがルール条件に失敗したか、20 回のルックアップの上限を超えたためにバッチが失敗すると、パイプラインが停止し、PERMISSION_DENIED エラーが返されます。

ただし、すでに commit されたバッチはロールバックされません。データベースは部分的に更新された状態のままです。クライアントは、変更されたドキュメントのリストなしでエラーを受け取るため、データをクエリして、どの書き込みが成功したかを調べる必要があります。

Cloud Audit Logs を使用したルールの失敗の診断

セキュリティ上の理由から、クライアント向けの PERMISSION_DENIED エラーでは、どのセキュリティ ルールの条件が失敗したか、または 20 回のルックアップの上限に達したかどうかは開示されません。

ルール評価の失敗を診断するには、プロジェクト管理者が Google Cloud コンソール([Firestore] > [モニタリング] または [ログ エクスプローラ])で Cloud Audit Logs を調べて、詳細な EvaluationError エントリを表示します。