Cloud Firestore セキュリティ ルールの条件の記述

このガイドはセキュリティ ルールの構造化ガイドに基づき、Cloud Firestore セキュリティ ルールに条件を追加する方法について説明します。Cloud Firestore セキュリティ ルールの基本については、スタートガイドをご覧ください。

Cloud Firestore セキュリティ ルールの主要な構成要素は条件です。条件とは、特定のオペレーションを許可するか拒否するかを決定するブール式です。セキュリティ ルールを使用して、ユーザー認証をチェックしたり、受信データを検証したり、データベースの他の部分にアクセスしたりするための条件を記述します。

認証

最も一般的なセキュリティ ルールのパターンの 1 つは、ユーザーの認証状態に基づいてアクセスを制御することです。たとえば、アプリにログインしているユーザーのみがデータの書き込みを許可されるようにすることができます。

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to access documents in the "cities" collection
    // only if they are authenticated.
    match /cities/{city} {
      allow read, write: if request.auth != null;
    }
  }
}

別のパターンとして、ユーザーが自分のデータのみを読み取りおよび書き込みできるようにすることも一般的です。

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure the uid of the requesting user matches name of the user
    // document. The wildcard expression {userId} makes the userId variable
    // available in rules.
    match /users/{userId} {
      allow read, update, delete: if request.auth != null && request.auth.uid == userId;
      allow create: if request.auth != null;
    }
  }
}

Firebase Authentication または Google Cloud Identity Platform を使用するアプリの場合、request.auth 変数には、データを要求しているクライアントの認証情報が含まれています。request.auth の詳細については、リファレンス ドキュメントを参照してください。

データの検証

多くのアプリでは、アクセス制御情報をデータベース内のドキュメントのフィールドとして格納します。Cloud Firestore セキュリティ ルールでは、ドキュメント データに基づいて動的にアクセスを許可または拒否できます。

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

resource 変数は要求されたドキュメントを参照します。resource.data はドキュメントに格納されているすべてのフィールドと値のマップです。resource 変数の詳細については、リファレンス ドキュメントをご覧ください。

データを書き込むときに、受信データと既存のデータを比較できます。このケースでルールセットが保留中の書き込みを許可する場合、request.resource 変数にはドキュメントの将来の状態が含まれます。ドキュメント フィールドのサブセットのみを変更する update オペレーションの場合、request.resource 変数にはオペレーション後の保留中のドキュメントの状態が含まれます。request.resource のフィールド値をチェックすることで、望ましくない、または整合性のないデータの更新を防ぐことができます。

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure all cities have a positive population and
    // the name is not changed
    match /cities/{city} {
      allow update: if request.resource.data.population > 0
                    && request.resource.data.name == resource.data.name;
    }
  }
}

他のドキュメントへのアクセス

セキュリティ ルールでは、get() 関数と exists() 関数を使用して、データベース内の他のドキュメントに対する受信リクエストを評価できます。get() 関数と exists() 関数は、完全なドキュメント パスを指定します。変数を使用して get()exists() のためのパスを構築する場合は、$(variable) 構文を使用して明示的に変数をエスケープする必要があります。

以下に示す例では、database 変数が match ステートメント match /databases/{database}/documents によってキャプチャされ、次のパスの形成に使用されます。

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      // Make sure a 'users' document exists for the requesting user before
      // allowing any writes to the 'cities' collection
      allow create: if request.auth != null && exists(/databases/$(database)/documents/users/$(request.auth.uid));

      // Allow the user to delete cities if their user document has the
      // 'admin' field set to 'true'
      allow delete: if request.auth != null && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
    }
  }
}

書き込みの場合は、getAfter() 関数を使用すると、トランザクションまたはバッチ書き込みが完了した後(トランザクションまたはバッチが commit される前)のドキュメントの状態にアクセスできます。get() と同様に、getAfter() 関数は完全なドキュメント パスを受け取ります。getAfter() を使用すると、トランザクションまたはバッチとしてまとめて実行する必要がある一連の書き込みを定義できます。

アクセス呼び出しの制限

ルールセット評価ごとのドキュメントのアクセス呼び出しには制限があります。

  • 単一ドキュメントに対するリクエストとクエリ リクエストの場合は 10。
  • 複数のドキュメントに対する読み取り、トランザクション、バッチ書き込みの場合は 20。各オペレーションには、上記の制限の 10 も適用されます。

    たとえば、3 つの書き込みオペレーションを含めたバッチ書き込みリクエストを作成するとします。セキュリティ ルールでは、2 つのドキュメントに対するアクセス呼び出しを使用して、それぞれの書き込みを検証します。この場合、各書き込みオペレーションがアクセス呼び出し制限数 10 のうちの 2 つを使用するため、バッチ書き込みリクエストはアクセス呼び出し制限数 20 のうちの 6 つを使用することになります。

いずれかの上限を超えると、アクセス拒否のエラーが発生します。一部のドキュメントに対するアクセス呼び出しはキャッシュされる場合があります。キャッシュされた呼び出しは制限数に計上されません。

これらの制限によるトランザクションやバッチ書き込みの影響の詳細については、アトミック オペレーションの保護のガイドをご覧ください。

アクセス呼び出しと料金

これらの関数を使用すると、データベースで読み取りオペレーションが実行されます。つまり、ルールによってリクエストが拒否された場合でもドキュメントの読み込みに対して課金されます。具体的な課金情報については、Cloud Firestore の料金をご覧ください。

カスタム関数

セキュリティ ルールの複雑化に伴い、一連の条件を関数としてラップし、ルールセット全体で再利用できるようにすることが必要になることがあります。セキュリティ ルールはカスタム関数をサポートしています。カスタム関数の構文は JavaScript に少し似ていますが、セキュリティ ルール関数はドメイン固有の言語で記述され、いくつかの重要な制限があります。

  • 関数には 1 つの return ステートメントのみを含めることができます。追加ロジックを含めることはできません。たとえば、ループを実行したり、外部サービスを呼び出したりすることはできません。
  • 関数は、定義されているスコープから、関数と変数に自動的にアクセスすることができます。たとえば、service cloud.firestore スコープ内で定義された関数は、resource 変数、および get()exists() などの組み込み関数にアクセスできます。
  • 関数は他の関数を呼び出すことはできますが、再帰はできません。コールスタックの深さは合計 10 に制限されています。
  • Rules バージョン v2 では、関数で let キーワードを使用して変数を定義できます。関数には最大 10 個の let バインディングを指定できますが、return ステートメントで終了する必要があります。

関数を定義するには function キーワードを使用します。関数は、0 個以上の引数を取ります。たとえば、上に示した例で使用した 2 つのタイプの条件を 1 つの関数にまとめることができます。

service cloud.firestore {
  match /databases/{database}/documents {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }

    match /cities/{city} {
      allow read, write: if signedInOrPublic();
    }

    match /users/{user} {
      allow read, write: if signedInOrPublic();
    }
  }
}

セキュリティ ルールで関数を使用すると、ルールが複雑化した場合に保守しやすくなります。

ルールはフィルタではない

データを保護してクエリの記述を開始したら、セキュリティ ルールはフィルタではないことにご注意ください。コレクション内のすべてのドキュメントに対してクエリを記述することはできません。Cloud Firestore によって返されるのは、現在のクライアントのアクセス権が設定されているドキュメントだけです。

たとえば、次のセキュリティ ルールがあるとします。

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

拒否: 結果セットには、visibilitypublic でないドキュメントを含めることができるため、このルールでは次のクエリは拒否されます。

ウェブ
db.collection("cities").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
    });
});

許可: 結果セットがルールの条件を満たすことが where("visibility", "==", "public") 句によって保証されるため、このルールでは次のクエリは許可されます。

ウェブ
db.collection("cities").where("visibility", "==", "public").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
        });
    });

Cloud Firestore セキュリティ ルールは、その結果の可能性に対して各クエリを評価し、クライアントが読み取り権限を持たないドキュメントが返された場合にリクエストを失敗させます。クエリは、セキュリティ ルールで設定されている制約に従う必要があります。セキュリティ ルールとクエリの詳細については、安全なデータのクエリをご覧ください。

次のステップ