42
51

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Firestoreのセキュリティルールとは

Posted at

#はじめに
Firestoreのセキュリティルールについて備忘録としてまとめます。

#セキュリティルールとは
このルールを設定することによって保管したデータのセキュリティを確保でき、悪意のあるユーザーからデータを守ることができます。

単純なルールだけでなく、アプリの構造と動作に応じて複雑なルールも自分で設定できます。
Cloud Firestore セキュリティ ルールを使ってみる | Firebase

このセキュリティルールの設定方法は

・Firebaseコンソール

Firebase CLI

の2種類から設定できます。

要するにブラウザから直接ルールを編集してルールを決めるか、自分のPCでファイルから設定を記述してルールを決めるかですね。

因みに自分は、Firebaseコンソールから設定しています。

#セキュリティルールの基本的な構成
デフォルトでは以下のようになっています。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if
          request.time < timestamp.date(2021, 3, 18);
    }
  }
}

順にコードの意味を理解していきましょう。

##rules_version = '2';
バージョンを指定しており、バージョン2が最新となっています。

バージョンの指定がないと自動的にバージョン1になります。

##service cloud.firestore
ルールを適用するプロダクト(サービス)を指定しています。

他のプロダクトとの競合を防ぐための設定なので、1つしか設定できません。

Firebase CLIを使用してFirestoreとFirebase Storageの両方のルールを定義する場合は
それぞれ別のファイルでルールを保守しなくてはいけません。

##match /databases/{database}/documents
ルールを適用するプロジェクト内でのFirestoreデータベースを指定しています。

現在、Firestoreでは1つのプロジェクトに1つのデータベースと決まっていて(default)という名前が付いています。
Cloud Firestore セキュリティ ルールを構造化する | Firebase

##match /{document=**}
先ほども登場しましたが、matchステートメントを使用することでパス(場所)を指定することが出来ます。

指定先はドキュメントのみしか指定できず、コレクションは指定できません

matchステートメントの次に記述されている括弧の部分はワイルドカード表記と呼ばれます。

// ワイルドカード表記
{document=**}

この表記方法を用いれば任意のドキュメントを指定することができます。

例えば、foodコレクションがあって、その中のドキュメントを指定するとします。

// foodコレクションの任意のドキュメントを指定する
// {}の中は自由に記述可能で、変数として使うこともできる
match /food/{meet} {
   allow read: if resource.data.name == meet;
}

※resource.data.name "ドキュメントに格納されている全てのフィールドと値のマップを参照できる"

// 特定のドキュメントも指定できる
// しかし、fruitsドキュメント以外の読み書きは拒否される
match /food/fruits {
}

このようになります。

前後しますが、何度も登場している{document=**}は再帰ワイルドカードと呼ばれ、普通のワイルドカードとは少し違います。

// 再帰ワイルドカード
{document=**}

// ワイルドカード
{meet}

これは例えば、各階層のサブコレクション全てにルールを適用したい場合などに使用すると非常に便利です。

例えば、foodコレクションより下の階層全てにルールを適用したい場合だと下記のようになります。

// foodコレクション以下の全てのドキュメントに対してルールを適用する
match /food/{document=**} {
}

// 普通のワイルドカードだとfoodコレクション内のドキュメントしか指定できず、それより下の階層は指定できない
match /food/{document} {
}

以上を踏まえてデフォルトで記述されている下記のコードの意味を考えると、プロジェクト内全てのドキュメントに対してルールが適用されるという意味合いになります。

// プロジェクト内すべてのドキュメントに対してルールを適用する
match /{document=**} {
}

##allow read, write: if
この部分が実際にセキュリティルールを指定する所で読み書きの許可条件を決めます。

分かりやすくするとこんな感じ↓

allow [アクセスタイプ(メソッド)]: if [条件];

allowステートメントの後に記述されているreadwriteなんかはアクセスタイプ(メソッド)として機能しており、ユーザーに対して読み書きのアクセス権を付与します。

大きく分けるとwritereadの2つなのですが、ここから更に細分化することが出来ます。

read "あらゆるタイプの読み取りリクエスト"
 - get "単一のドキュメントを対象とした読み取りリクエスト"
 - list "クエリとコレクションを対象とした読み取りリクエスト"

write "あらゆるタイプの書き込みリクエスト"
 - create "新しいドキュメントの書き込み"
 - update "既存のドキュメントへの書き込み"
 - delete "データの削除"

これらのアクセスタイプ(メソッド)を、必要に応じて加えていく感じですね。

そして、ifの後には様々な条件を書き足すことができます。

デフォルトだと、改行されていて分かりにくいですがrequest.time < timestamp.date(2021, 3, 18);が条件ですね(後ほど説明)。

条件のよくある例として、Bool値を記述して読み書きしてもいいかを決めます。

// 読み書きを許可する
allow read, write: if true;

// 読み書きを拒否する
allow read, write: if false;

デフォルトではBool値が省略されており、その場合は無条件でtrueになります。

// 無条件でtrueが返される
allow read, write: if

##request.time < timestamp.date(2021, 3, 18);
先ほどのifの後に続く条件です。

意味としては、2021/3/18の日付より過去なら読み書き可能という感じになっています。

#Firebase Authenticationとの連携
FirestoreとFirebase Authenticationを連携して、より強固なセキュリティルールを設定することが出来ます。
セキュリティ ルールと Firebase Authentication | Firebase

※Firebase Authenticationがアプリにて実装されていることが大前提です。

##ユーザーを識別する
Firestoreにはユーザーを識別するために使用できる変数がいくつか用意されています。

例えばログインプロバイダにメール/パスワードを用いている場合は、

uid "リクエストしているユーザーに割り当てられたユーザーID"
auth.token.email "アカウントに関連付けられているメールアドレス"

などが使用できます。

他にも色々、変数が存在しますので公式ドキュメントを読んでみて下さい。

##ルールでユーザー情報を使用する
早速、セキュリティルールにユーザー情報を使用してみましょう。

下記では、usersコレクションの任意のドキュメントを指定しており、

service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      // アカウントが存在し、auth.uid変数とリクエスト送信者のユーザーIDが一致していれば読み書きを許可する
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

という構成になっています。

##requst.auth != null
request.authは、Firebase Authenticationから取得した認証情報を表しており、ここではアカウントが存在していればという意味になります。

##request.auth.uid == userId
この部分は、アカウントのuidとFirestoreに保存してあるユーザーIDを照合して一致していれば読み書きを許可します。

match /users/{userId} {
  allow read, write: if request.auth.uid == userId;
}

#関数
セキュリティルールには関数を定義することができ、一連の条件をまとめることが出来ます。

service cloud.firestore {
  match /databases/{database}/documents {

    // アカウントが存在していればtrueを返す関数
    function isAuthentication() {
      return request.auth.uid != null;
    }

    // このように複数の場所から条件を指定できる
    match /cities/city {
      allow read: if isAuthentication();
    }
    match /users/{user} {
      allow read, write: if isAuthentication();
    }
  }
}

関数に引数を割り当てることも可能です。

function isMe(userEmail) {
    return request.auth.token.email == userEmail;
}

match /users/{userEmail} {
    allow write, read: if isMe(userEmail);
}

#おわりに
とにかく公式ドキュメントを読もう。

次は、実際にFirestoreとFirebase Authenticationを連携してユーザーによって保存されているデータを分ける方法をまとめようと思います。

#参考記事
Cloud Firestore セキュリティ ルールを構造化する
Firestoreセキュリティルールを読み解く
Firestore セキュリティルール ~入門編~

42
51
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
42
51

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?