Firebaseのセキュリティ(クライアント/サーバーでの違い)とローカル実行時の接続方法を正しく理解する

これは、[Firebase Advent Calendar 2020](https://qiita.com/advent-calendar/2020/firebase) の2日目の記事です。

## これは何?

Firebaseを利用してアプリやバックエンド開発を行う著者が、利用当初わかりにくかったことを整理しました😌

- `Firebase`には、Clientからの接続と管理者として接続する方法がありますが、この違いを整理します。
  - これにより、`Firebase`を商用利用する際の留意点や、鍵の管理について理解できます。
- `Firebase`にローカルで接続する方法を扱います。
  - これにより、たとえば`Cloud Functions`をローカルEmulatorで起動する際に、開発用のFirebaseに接続できるようになります。

## Firebaseの利用方法の違い

Firebaseの接続に利用する鍵は下記の２種類があります。これらの違いが分かりにくかったので整理します。

- API Key (APIキー)
- Private key (秘密鍵)



違いについて、先にまとめます。

| 鍵の種別    | 利用用途                                                                              | 備考                                                                                                                                               |
| ----------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| API Key     | 外部に公開しユーザーが利用するようなClientとしての利用を想定。Webサービス、アプリなど | API Key自体は公開してもよいが、適切にセキュリティルールを設定することで、ユーザーごとにRead/Writeの権限を設定する。 **Firebase SDK** を利用。                                |
| Private Key | バックエンドの処理など、外部ユーザーは利用しない内部処理                              | Private Key はセキュリティルールにかかわらずアクセスできてしまうため扱いに注意する(公開しないこと)。 **Firebase Admin SDK**を利用。 |


### API Key - Clientとして利用する場合に利用する


API Key の利用については、ドキュメント: [Firebase を 使ってみる > Web](https://firebase.google.com/docs/web/setup?hl=ja) などに、利用用途の記述があります。


> Firebase JavaScript SDK をウェブアプリで使用する手順について説明します。また、Node.js デスクトップや IoT アプリケーションなどのエンドユーザー アクセスのクライアントとして使用する場合も、この手順に従ってください。


つまり、簡単にいうと、外部に公開しユーザーが利用するようなClientとしての利用を想定している場合は、API Keyを利用するようにします。

上記ドキュメント通りに、API Keyを発行したら、 [Firebase Configオブジェクト](https://firebase.google.com/docs/web/setup?hl=ja#config-object) のとおり、API Keyを設定します。これは **公開前提** の値となります。

```
var firebaseConfig = {
  apiKey: "API_KEY",
...
};
```


え、公開してしまって良いの？だれでもデータにアクセスできそうだけど大丈夫？！😱 と不安になりますが、大丈夫です。下記ドキュメントを読むとよくわかりますが、 **セキュリティルール** を必ず設定して、ユーザーごとに権限を適切に設定することが大切となります。

公式ドキュメント:[FirebaseのAPIキーの使用と管理について学ぶ](https://firebase.google.com/docs/projects/api-keys#api-keys-for-firebase-are-different)より引用：

> Unlike how API keys are typically used, API keys for Firebase services are not used to control access to backend resources; that can only be done with Firebase Security Rules.

つまり、下記のように明記されています。

>  FirebaseのAPIキーは、一般的なAPIキーとは異なる。FirebaseサービスのAPIキーはバックエンドリソースへのアクセスを制御するためには使用されず(=一般公開してもよいもので)、Firebaseのセキュリティルールでのみ可能


この辺りの話題は、先日のFirebase Summit2020でも [Serverless security modeling in Firebase](https://www.youtube.com/watch?v=ASugxDQPm3A) のトークで `06:39  API key management` あたりで述べられており、参考になります。

セキュリティルールについて簡単に触れると、
[安全でないルールを修正する](https://firebase.google.com/docs/firestore/security/insecure-rules?hl=ja) にあるように、読み込みできるデータ、書き込みできるデータなどの条件を設定することでセキュリティが担保できる仕組みです。

```
// わかりやすく例をすこし変えてみましたが、
// ユーザーデータは自分のデータしか読み書きできないような制約を設定します
service cloud.firestore {
  match /databases/{database}/documents {
    // Allow only authenticated content owners access
    match /users/{userId} {
      allow read, write: if request.auth.uid == request.resource.data.author_uid
    }
  }
}
  
```

### Private Key - サーバーサイドで利用する

これまで説明したClient側の利用と反して、サーバーサイドの処理では **セキュリティールール無視** でアクセス可能となります。[^adminSDK]

そのため、サーバーサイドで利用するPrivate Key は扱いに注意が必要です。たとえば、Githubなどに誤ってコミットして一般公開してしまうなどしないようにしてください。

[^adminSDK]: この仕様について、公式ドキュメントでの記載を確認できませんでしたが、FirebaseやFlutterに関する多くの技術発信をされている[monoさんのブログ](https://medium.com/google-cloud-jp/firestore3-9518331f8748) にも記載があります。 `セキュリティルールはあくまでクライアントからのアクセスルールを定義するものであって、Admin SDKからの操作には何の影響も及ぼしません。`

サーバーサイドの処理では、 **Firebase Admin SDK** を利用し、言語ごとの実装はドキュメント[サーバーに Firebase Admin SDK を追加する](https://firebase.google.com/docs/admin/setup?authuser=0) に記載があります。


## Firebaseへの接続

### ローカルのAdmin SDKからFirebaseへ直接接続する

たとえばCloud FunctionsをローカルEmulatorで起動する際に、開発用のFirebaseに接続できるようにしたい、というニーズはよくあると思います。

下記の２種類の方法があります。

- 1. 環境変数 **GOOGLE_APPLICATION_CREDENTIALS** の設定
- 2. 環境変数 **FIREBASE_CONFIG** の設定


環境変数 **GOOGLE_APPLICATION_CREDENTIALS** に、PrivateKey(JSONファイル)のパスを指定する方法です。先ほどの、 `Cloud FunctionsをローカルEmulatorで起動する際に、開発用のFirebaseに接続できるようにしたい` ニーズに利用するのはこちらの方法を使うのが楽でしょう。


環境変数 **FIREBASE_CONFIG** に、PrivateKeyのJSONをそのままいれるを入れる方法もあります。たとえば、CI環境でAdmin SDKを利用するような場合に有用そうです。これらの環境変数に関するドキュメントは、 [こちらです](https://firebase.google.com/docs/admin/setup?authuser=0) です。




### ローカルでFirebase Emulatorへ接続する

さきほどは、ローカルから直接Firebaseへ接続する方法を紹介しました。下記のような場合には、Firebaseに接続するのではなく、ローカルで起動したFirebase Emulatorへ接続する方法もあります。

- テストデータのみの環境で動作させたい
- たくさんのデータの更新を行うため、チームで共有しているFirebaseのデータを更新したくない

Firebase Emulatorへの接続は、`Firebase SDK` では、 `db.useEmulator("localhost", 8080);` のようにします。

`Firebase Admin SDK` では、下記の環境変数を設定します。

```
export FIRESTORE_EMULATOR_HOST="localhost:8080"  //Firestore
export FIREBASE_AUTH_EMULATOR_HOST="localhost:9099" //AUTH
```

これらのドキュメントは、[こちら](https://firebase.google.com/docs/emulator-suite/connect_auth#instrumentauth) や [こちら](https://firebase.google.com/docs/emulator-suite/connect_firestore)です。

## さいごに

**Cloud Firestore** を利用されている方は、運用ツール [`fsrpl` が便利](https://zenn.dev/mtskhs/articles/fsrpl-replication-tool-for-testing)ですので、利用してみてください :smile: 
また、技術書典10にて、Firestore*Testについての書籍を執筆中です。お楽しみに！
こちらに書籍が掲載されます👉https://techbookfest.org/organization/5712574639243264/