Firebaseデータベースのセキュリティルールの基本

記事
IT・テクノロジー

Firebaseデータベースのセキュリティルールの基本

Firebaseのデータベースを利用する場合、セキュリティルールをきちんと設定する事が重要です。プログラムでアクセスの制限をかける事はある程度可能です。しかし、セキュリティルールでアクセス権を適当に設定してあれば、不必要なアクセスをより確実に防ぐことが可能になります。

Firebaseのセキュリティルールの仕組み

Firebaseのセキュリティルールはアクセスタイプごとに設定するようになっています。 アクセスタイプは:
* 読み込み(read)
* 書き込み(write)

に分けられます。
さらに細かい分類に分ける事ができます。
読み込みはさらに2つのタイプ、
* get
* list

に分ける事ができます。 書き込みはさらに3つのタイプに分けられます。
* create
* update
* delete
まとめると以下の表の様になっています。Firebaseのドキュメントでも同じ情報がまとめられています。セキュリティルールはストレージへのアクセスも同じ記法で書けるので以下の表ではファイルも含まれています。ここでは、データベース(特にCloud Firestore)について解説していきます。
* get:ドキュメントまたはファイルへのアクセス
* list:コレクションやクエリーのアクセス
* create:ドキュメントの新規作成
* update:既存のドキュメントまたはファイルの更新
* delete:データの削除


ルールを適用するサービスを指定
既に触れたように、セキュリティルールはデータベースにも外レージにも適用できます。したがって、何のルールであるかをサービスを指定して明示します。 これはルールの最初の部分で指定します。2020年10月現在最新のセキュリティルールのバージョンはVersion 2です。そこで、この記事ではVersion2を前提に書いています。 ルールの冒頭部分は以下の様になります。
Cloud Firestoreにルールを適用する場合は以下のような書き出しになります。
rules_version = '2';
service cloud.firestore {
   .......
}

ルールを適用するデータを特定する
FirebaseのドキュメントではMatchという分類で説明しています。 要は、どのパス(path)のデータにルールを設定するかという指定です。
Firebase Cloud Firestoreの場合は、このパスはコレクション(collection) と ドキュメント(documentの組合わせで指定する事になります。
Firebase Cloud Firestoreにアクセスのリクエストがあった時に、そのリクエストのパスがルールに適合するかを調べて最終的なアクセスの可否を決定する仕組みです。
Firebase Cloud FirstoreのデータベースをFirebaseコンソールで指定した場合に2つの選択肢があります。
* プロダクションモード(production mode)
* テストモード(test mode)

プロダクションモードで設定されるルール
データベースをプロダクションモードで作成した場合、Firebaseが標準で設定するルールです。 「match /{document=**}」というのは、Firebase Cloud Firestoreの全てのドキュメントが対象になります。したがって、このルールは全てのドキュメントにアクセスできないという設定になります。
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if false;
    }
  }
}
テストモードで設定されるルール
このルールも、対象となるのはFirebase Cloud Firestoreの全てのドキュメントという点ではプロダクションモードと同じですが、アクセスの条件が違います。 アクセスは、アクセス時点の日付(時間)が予め指定した日時よりも前ならば、読み書きを許可するという物です。
これは、開発当初は、Firebaseのプロジェクトのアプリをインターネット上に公開しないで開発する場合が殆どのため、厳密なルールを必要ないので、この期間内は全てのアクセスを許可してしまおうという物です。
開発中であっても、Firebaseのプロジェクトで作成しているアプリをインターネットに公開する場合は注意が必要です。通常はインターネットに公開する前にセキュリティルールの変更が必要です。
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if
          request.time < timestamp.date(2020, 11, 23);
    }
  }
}


具体的な例で解説

分かり易くするためにもう少し具体的な例で説明します。
特定のコレクション毎にルールを指定する
Firebase Cloud Firestoreに「students」というコレクションを作成したとします。例えば、このコレクションには学生の住所や名前が入ったドキュメントを格納しているとします。
この場合、このドキュメントに全ての許可を与える場合には
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if false;
    }
    match /students/{studentId} {
        allow read, write;
    }
  }
}
のように記述すれば。このコレクションに対しては、全ての利用者が読み書きを出来る事になります。大切なのは、最初に全てのドキュメントに対して禁止の設定をしている点です。
セキュリティルールは上から順番に評価されて行きます。最初に「Match(一致)」する部分が、「match /{document=**}」の部分ですがここでは、全てのコレクションに対してアクセスが禁止されています。次に、アクセスが「/students」のコレクションだった場合は、「match /students/{studentId}」の部分のルールがこのコレクションに適用されるので、ここでは全ての利用者に読み書きを許可しているのでアクセスが可能になるという仕組みです。
「{studentId}」の部分は実は不要ですが、後でドキュメント毎のアクセスの設定をする際に便利なので通常は「studentId」という変数を設定しています。
特定のユーザーにアクセスを許可する
前の例では全ての利用者に読み書きを許可していますが、例えばユーザー毎にアクセスの権限を変える事もできます。例えばあるユーザーを「管理者」として設定する場合です。
管理者は全てのコレクションの読み書きが出来る設定で、一般の利用者は「students」のコレクションの読み込みのみを許可するという場合です。
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.auth.uid == "TVXoXBXz0Zbf3iy1uqb1UM5F6zu2";
    }
    match /students/{studentId} {
        allow read;
    }
  }
}
上のようなルールを設定すると、「uid」が「TVXoXBXz0Zbf3iy1uqb1UM5F6zu2」のユーザーのみ管理者としてFirebase Cloude Firestoreの全てのコレクションに読み書きの権限を持つことになります。そして、一般の利用者(uidがTVXoXBXz0Zbf3iy1uqb1UM5F6zu2以外)は、「students」のみ読み込みの許可を持つことになります。
セキュリティルールでは幾つかの機能が用意されていて、「request.auth.uid」はリクエストを要求しているユーザーの「uid」を取得できます。他にもFirebase Cloude Firestoreのデータを参照したりする事も可能です。
例えば、ログインしているユーザーという条件の場合は
request.auth != null
のような条件をつければ、ログインしているユーザーに対する権限を設定できます。

お問合せフォームの例

何回かFirebaseのデータベース機能(Cloud Firestore)を作成する例を紹介していますが、その場合のセキュリティルールについて解説します。
お問合せフォームを利用する人の役割(ロール:role)は2つです。
* 一般利用者:お問合せをする人
* 管理者:お問合せを管理、回答する人

です。この場合必要な権限は以下のようになります。

* 一般利用者 新規作成(create) ログインしないでお問合せを許可
* 管理者  読み込み(read)
      書き込み(write)

対象となるFirebase Cloud Firestoreのコレクションは「queries」だとすると、セキュリティールールは以下の様になります。お問合せフォームの管理者は、Firebaseのプロジェクトの管理者と別のユーザーがいるとして、そのお問合せフォームの管理者のuidが「c1CC1aCcIHV0vuc7UTbzvljHUF33」の場合を考えます。
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.auth.uid == "TVXoXBXz0Zbf3iy1uqb1UM5F6zu2";
    }
    match /queries/{queryId} {
        allow read;
        allow write: if reques.auth.uid == "c1CC1aCcIHV0vuc7UTbzvljHUF33";
    }
  }
}
上の様なルールを適用すれば、各ユーザーの役割ごとに細かなルールが設定できます。

セキュリティールールで何が制限されるか?

フロントエンドのJavascript(Typescript)のソースコードは、基本的にWebブラウザから参照可能です。従って、Firebase Cloud Firestoreのコレクション名などを特定する事は技術的には可能です。一応プログラムの実行を許可するドメインを指定するなどして、プログラムの実行に制限をかける事は可能なので、簡単に第三者がFirebase Cloud FirestoreにWebサーバー以外からのコードを実行する事は簡単ではありません。しかし、「localhost」は開発時に利用する場合が殆どで、これが実行を許可するドメインに入っていると誰でもプログラムの実行は可能です。
こうした場合でも、セキュリティルールでユーザー毎の権限が設定されているとアクセスを制限できます。
firebase.firestore().collection("queries").get().then((snapshot) => {
    snapshot.forEach((doc) => {
        // ドキュメントの内容をコンソールに表示
        console.log(doc.data())
    })
}).catch((error) => {
    // エラーの場合
    console.log(error.message);
})
コレクション名を知っていれば上のようなプログラムを書くことができます。このプログラムを仮に実行できたとしても、ユーザー毎のセキュリティルールが設定されている場合、このアクセスはエラーになります。つまり、データの取得はできないという事になります。

まとめ

Firebase Cloud Firestoreを利用する場合のセキュリティルールの書き方の基本を簡単に説明してみました。
データベースのアクセスはプログラム(Javascript/Typescript)でもある程度制限する事は可能です。また、プロジェクトの設定をきちんと行う事でより安全に運用する事も可能です。 しかし、インターネット上で情報を扱う場合はより慎重に可能な限りの措置を取る事が非常に重要になります。 従って、Firebaseが提供しているセキュリティルールはその中でも、重要な役割を担っています。多くの場合、開発者は必要な機能の実装に目が行きがちになります。しかし、アプリやサービスのセキュリティは運営側、利用者側双方でとても重要な項目です。
データベースのデータ構造を検討する際にもセキュリティルールを適用しやすいデータ構造を採用する事はより万全なセキュリティを構築する上でもとても重要です。Firebaseのセキュリティルールをきちんと理解する事は、より安全なWebサービス、Webアプリを設計・実装する上で不可欠です。
サービス数40万件のスキルマーケット、あなたにぴったりのサービスを探す