Firebaseストレージのセキュリティルールはトリッキー!!

記事
IT・テクノロジー

Firebaseストレージのセキュリティルールはトリッキー!!

今日の記事は、Firebaseストレージのセキュリティルールに関してです。 Firebaseのデータベース同様、Firebaseのストレージを利用する際はセキュリティルールを設定する事は同じように大切です。しかし、いくつかわかりにくい点がFirebaseのストレージにはあるのでこの記事で解説しています。
最初にプロジェクトを作成してFirebaseのストレージの設定をしたときに標準で設定されるルールは以下のようなルールです。
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}
このルールの設定は、全てのFirebaseのストレージのアクセスには、Firebaseにログインすることが必要という設定です。「request.auth != null」の意味は、Authentication(ユーザーの認証)が完了しているという意味です。

Webアプリの制限事項

FirebaseはWebアプリ以外のiPhone(iOS)やAndroidのスマホのアプリにも利用できます。しかし、ここではWebアプリ(Webブラウザを利用したアプリ)の場合を想定しています。Webブラウザで動作する、Webアプリは、セキュリティを確保するために、Webブラウザでいろいろな制限を受けています。
基本的にはWebブラウザが動いているデバイス(PCやスマホ)の他のファイルにはアクセスできません。ダウンロードはできますが、Webブラウザの設定によって、自動でダウンロードを容認する場合と、利用者が了解したときのみダウンロードするなど扱いも変わってきます。
Firebaseストレージに保存されているデータは基本的にはファイルです。
Web版のFirebaseストレージでできることは、
* データからファイルを作ってFirebaseストレージに保存
* 端末のファイルを選択して、Firebaseストレージにアップロード
* FirebaseストレージのファイルのURLを取得してダウンロード
* Firebaseストレージのファイルの削除
* ファイルやフォルダのリストの取得

といった操作です。Firebaseコンソールからは、フォルダの作成も可能ですが、クライアント側からはフォルダの作成をするAPIはありません。ただし、フォルダを含むファイルの指定は可能です。必要なフォルダが存在しない場合はファイルをアップロードや保存する場合は勝手にフォルダを作成してくれます。ファイル自体の削除はできるので、フォルダをプログラムから作成することは可能ですが、FirebaseはAPIとしては用意していません。

Firebaseストレージに保存されているファイルの中身を見るには?

Firebaseストレージに保存されているファイルの中身を見るAPIはFirebaseでは用意されていません。
Firebaseのストレージに保存されたファイルの中身を見る手っ取り早い方法は、対象となるファイルのURLを取得して、「fetch()」を使って中身を読み取る方法です。中身がテキストファイルの場合は、
fetch(url).then((response) =>{
    console.log(response.text());
})

のようにすれば、テキスト情報をプログラムで取得可能です。
FirebaseのストレージのAPIを使ってダウンロードのためのURLを取得するには、「getDownloadURL()」を使います。
ファイルがあるフォルダへのパスととファイルの名前がわかれば、ファイルへのリファレンス(firebase.storeage.Reference)が取得できるので、それを使えば簡単にURLを取得できます。
static getURL(folder: string, file: string): Promise<string | undefined> {
    return new Promise((resolve) => {
      firebase
        .storage()
        .ref()
        .child(folder + "/" + file)
        .getDownloadURL()
        .then((url) => {
          resolve(url);
        })
        .catch((error: any) => {
          resolve(undefined);
        });
    });
  }

こんな感じで取得できます。

ダウンロードURLの盲点

実はこのダウンロードURLはちょっと曲者です。 このダウンロードURLの取得には、Firebaseストレージへの読み込みの権限が必要です。
ところが、この取得したURLは毎回同じURLで、このURLを知っている場合、セキュリティルールで読み込みの権限がない場合でも、ファイルにアクセスすることが可能です。
ここがよく混乱するところです!
セキュリティルールを設定してFirebaseストレージのファイルのアクセスに制限をかけているつもりでも、このURLを知っていれば誰でもファイルにアクセスできてしまうという場合があるからです。

プログラムからのアクセスしかブロックできない!

このセキュリティルールは、Firebaseのバケット(bucket)へのアクセスの権限をチェックしています。このバケットへのパスは、「gs://test-project-d1c94.appspot.com」のようにあらわされます。一見URLの様ですがWebブラウザでこのパスを指定してもアクセスすることはできません。
WebブラウザからファイルをダウンロードするためにはURLが必要になります。 したがって、Firebaseストレージのファイルをダウンロードするような仕組みを作る場合,URLを取得して公開してしまうと、そのURLを知る人全てがそのファイルにアクセスできるようになります。
無料でオンライン上のファイルを公開/配布する場合は大きな問題ではありませんが、限定的に限られた人のみに配布したい場合には問題になる可能性がある仕組みです。
回避策はありますが、フロントエンド(Webブラウザ側)のみの実装では有効な解決策はありません。回避にはサーバー側(バックエンド)の実装が必要です。
(*)今回はフロントエンドの実装を前提に記事を書いていますので、こちらの詳細は別の記事を書く予定です。ご了承ください。
サーバー側のストレージアクセスは基本的に、Google Cloud Storage APIを使用してアクセスします。アクセスのやり方が、クライアント側(フロントエンドからのFirebase利用の場合)と異なりますので注意が必要です。(混乱を避けるために、記事を分けている理由です)

CORSの問題

CORS(Cross-Origin Resource Sharing)が問題になる場合もあります。Firebaseのドキュメントにも書かれていますが、ブラウザ内で直接データをダウンロードする場合、CORSの設定が正しくされていないとエラーになることがあります。 これは異なるドメインからのアクセスをセキュリティを確保する観点から最新版のWebブラウザはブロックしてしまうためです。
これを回避するためには、CORSの設定を行う必要があります。この設定は、「gsutil」というツールを使って行います。実際はFirebaseのストレージ機能は、Googleが提供しているCloud Storageと共用されているようで、Cloud Storage用の管理ユーティリティです。
gsutilのインストール
わかりやすいのはGoogle Cloud SDKをインストールする方法です。Google Cloud SDKにはこのユーティリティも含まれているので確実で簡単な方法です。インストールの方法は、Google CloudのドキュメントのCloud SDKクリックスタートにWindows / MacOS / Linuxのセットアップの方法の詳細が説明されていますので参考にされてください。
CORSの設定
Google Cloud SDKのインストールが完了したら、設定を行うFirebaseのプロジェクトフォルダに移動して以下の設定を行います。基本的に、FirebaseでWebホスティングを行う前提でこの記事を書いていますので、Webホスティングを行うためのフォルダで「firebase init」を実行したフォルダーになります。
このフォルダに「cors.json」というファイルを作成します。このファイルが、CORSの設定を行うファイルです。Firebaseのドキュメントにも説明があります。
Firebaseのドキュメントのサンプルを引用すると以下のような内容になります。
[
  {
    "origin": ["*"],
    "method": ["GET"],
    "maxAgeSeconds": 3600
  }
]
この設定はすべてのオリジン(ドメイン)からのアクセスを許可するというものです。 「"origin":[""」の「」の部分を変えて特定のドメインをしていすればそのドメインからのアクセスを許可するような設定になります。 Firebaseのホスティングのエミュレータによる、「localhost:5000」からのアクセスを許可するには、
[
  {
    "origin": ["localhost:5000"],
    "method": ["GET"],
    "maxAgeSeconds": 3600
  }
]
のように記述します。特定ドメインからのアクセスのみを許可する場合には、
[
  {
    "origin": ["sv-sw.com"],
    "method": ["GET"],
    "maxAgeSeconds": 3600
  }
]
のようにドメインを指定します。実際の設定では、必要なドメインのみを許可するような設定にした方が、セキュリティ上はより安全に運用できます。
cors.jsonを作成した後で、Google Cloud SDKShellでFirebaseプロジェクトフォルダ(cors.jsonがあるフォルダ)で以下のコマンドを実行します。
$ gsutil cors set cors.json gs://<設定するプロジェクトのストレージバケット(bucket)>
です。プロジェクトのストレージバケットは、Firebaseコンソールで確認できます。
これで、CORSのエラーを回避できます。

まとめ

Firebaseのストレージ機能の実装は、Firebaseのデータベースに比べると少しわかりにくく複雑です。特に、Firebaseのストレージのセキュリティルールの設定は、ダウンロードで使用するURLでのアクセスは適用外になるので注意が必要です。
また、異なるドメインからのアクセスにも対応する必要があります。(CORSの設定をGoogle Cloud StorateのSDKの機能を使って行います)
より実践的な実装には、サーバー側(バックエンド)での処理と組み合わせた方が自由度の高い実装が可能です。サーバー側の実装の記事は別途記事を書くことを予定していますので、そちらを参照してください。
サービス数40万件のスキルマーケット、あなたにぴったりのサービスを探す