Firebase Cloud Storage セキュリティ ルールで条件を使用する

このガイドは、Firebase セキュリティ ルール言語の基本構文に関するガイドに基づいて作成されており、Cloud Storage 用の Firebase セキュリティ ルールに条件を追加する方法について説明します。

Cloud Storage セキュリティ ルールの主要な構成要素は、条件です。条件とは、特定のオペレーションを許可するか拒否するかを決定するブール式です。基本的なルールとしては、リテラル true および false を条件として使用すれば、十分な機能を発揮します。ただし、Cloud Storage 用の Firebase セキュリティ ルール言語では、より複雑な条件を記述でき、次の操作を行うことができます。

  • ユーザー認証を確認する
  • 受信データを検証する

認証

Cloud Storage の Firebase セキュリティ ルールを Firebase Authentication と統合すると、Cloud Storage にユーザーベースの強力な認証を実装できます。これにより、Firebase Authentication トークンのクレームに基づいてアクセスを細かく制御することができます。

認証されたユーザーが Cloud Storage に対してリクエストを行うと、request.auth 変数にユーザーの uidrequest.auth.uid)と Firebase Authentication JWT(request.auth.token)のクレームが入力されます。

また、カスタム認証の使用時には、追加のクレームが request.auth.token フィールドに表示されます。

認証されていないユーザーがリクエストを行うと、request.auth 変数は null になります。

このデータを使用する場合、次のような一般的な方法で認証を使用してファイルを保護できます。

  • 公開: request.auth を無視
  • 認証済み非公開: request.authnull でないことを確認
  • ユーザー非公開:request.auth.uid とパス uid が同じであることを確認
  • グループ非公開: カスタム トークンのクレームと選択されたクレームが一致することを確認、またはファイル メタデータを読み取ってメタデータ フィールドが存在しているか確認

公開

request.auth コンテキストを考慮しないルールは、ユーザーの認証コンテキストを考慮しないため、public ルールとみなすことができます。このようなルールは、ゲームアセット、サウンド ファイル、その他の静的コンテンツといった一般公開データを表示するのに便利です。

// Anyone to read a public image if the file is less than 100kB
// Anyone can upload a public file ending in '.txt'
match /public/{imageId} {
  allow read: if resource.size < 100 * 1024;
  allow write: if imageId.matches(".*\\.txt");
}

認証済み非公開

アプリケーションの認証済みユーザーに対してはデータを閲覧可能にし、未認証ユーザーに対しては閲覧不可能にしたい場合があります。request.auth 変数はすべての未認証ユーザーに対して null であるため、認証を要求するために必要なのは request.auth 変数が存在することの確認だけです。

// Require authentication on all internal image reads
match /internal/{imageId} {
  allow read: if request.auth != null;
}

ユーザー非公開

request.auth の最も一般的な使用例は、プロフィール写真のアップロードからプライベート ドキュメントの読み取りまで、ファイルに対する細かいアクセス権限を個々のユーザーに提供することです。

Cloud Storage 内のファイルはファイルへのフル「パス」を保持しています。そのため、ファイル名接頭辞内に、ルール評価時のチェックが可能な一意のユーザー識別情報(ユーザーの uid など)を入れるだけで、ユーザーによるファイルの制御が可能となります。

// Only a user can upload their profile picture, but anyone can view it
match /users/{userId}/profilePicture.png {
  allow read;
  allow write: if request.auth.uid == userId;
}

グループ非公開

ユーザー非公開と同じくらい一般的な使用例として挙げられるのが、オブジェクトに対するグループ アクセス権限を許可することです。たとえば、共有ドキュメント上での共同作業を複数のチームメンバーに許可します。これを行う方法はいくつかあります。

  • グループ メンバーに関する追加情報(グループ ID など)を含む Firebase Authentication カスタム トークンを作成する
  • グループ情報(グループ ID、認可された uid のリストなど)をファイル メタデータに含める

このデータがトークンまたはファイル メタデータに格納されると、ルール内から参照できるようになります。

// Allow reads if the group ID in your token matches the file metadata's `owner` property
// Allow writes if the group ID is in the user's custom token
match /files/{groupId}/{fileName} {
  allow read: if resource.metadata.owner == request.auth.token.groupId;
  allow write: if request.auth.token.groupId == groupId;
}

リクエストの評価

アップロード、ダウンロード、メタデータの変更、削除は、Cloud Storage に送信される request を使用して評価されます。上述した request.auth オブジェクトに含まれるユーザーの一意の ID と Firebase Authentication ペイロードに加え、request 変数には、リクエストが行われているファイルパスとリクエストの受信時刻も含まれます。さらに、書き込みリクエストの場合は、新しい resource 値も含まれます。

また、request オブジェクトには、ユーザーの一意の ID と request.auth オブジェクト内の Firebase Authentication ペイロードも含まれます。これらについては、このドキュメントのユーザーベースのセキュリティで詳しく説明します。

request オブジェクトのプロパティの完全なリストは次のとおりです。

プロパティ 説明
auth マップ<文字列, 文字列> ユーザーがログイン済みの場合、uid(ユーザーに一意の ID)と token(Firebase Authentication JWT クレームのマップ)を提供します。それ以外の場合は、null になります。
params マップ<文字列, 文字列> リクエストのクエリ パラメータを含むマップです。
path パス リクエストの実行先のパスを表す path です。
resource マップ<文字列, 文字列> write リクエスト時にのみ指定される新しいリソース値です。
time タイムスタンプ リクエスト評価時のサーバー時刻を表すタイムスタンプです。

リソースの評価

ルールを評価するときに、アップロード、ダウンロード、変更、または削除するファイルのメタデータも評価したい場合があります。メタデータを評価すると複雑で効果的なルールを作成できるので、特定のコンテンツ タイプを持つファイルだけをアップロードしたり、特定のサイズよりも大きなファイルだけを削除したりすることができます。

Cloud Storage 用の Firebase Storage セキュリティ ルールにおいて、ファイルのメタデータは resource オブジェクトを通して提供されます。このオブジェクトには、Cloud Storage オブジェクトに示されるメタデータの Key-Value ペアが含まれます。read または write リクエストでこれらのプロパティを検査することで、データの整合性を確認できます。

write リクエスト(アップロード、メタデータの更新、削除など)では、現在リクエストパスに存在しているファイルのファイル メタデータを含む resource オブジェクトに加え、書き込みが許可された場合に書き込まれるファイル メタデータのサブセットを含む request.resource オブジェクトを使用することもできます。この 2 つの値を使用してデータの整合性を確認したり、ファイルのタイプやサイズといったアプリケーションの制約を適用したりできます。

resource オブジェクトのプロパティの完全なリストは次のとおりです。

プロパティ 説明
name 文字列 オブジェクトのフルネームです。
bucket 文字列 このオブジェクトが置かれているバケットの名前です。
generation int このオブジェクトの Google Cloud Storage オブジェクト生成です。
metageneration int このオブジェクトの Google Cloud Storage オブジェクト メタ世代です。
size int オブジェクトのサイズです(バイト単位)。
timeCreated タイムスタンプ オブジェクトの作成時刻を示すタイムスタンプです。
updated タイムスタンプ オブジェクトの最終更新時刻を示すタイムスタンプです。
md5Hash 文字列 オブジェクトの MD5 ハッシュです。
crc32c 文字列 オブジェクトの crc32c ハッシュです。
etag 文字列 このオブジェクトに関連付けられた etag です。
contentDisposition 文字列 このオブジェクトに関連付けられたコンテンツの配置です。
contentEncoding 文字列 このオブジェクトに関連付けられたコンテンツ エンコーディングです。
contentLanguage 文字列 このオブジェクトに関連付けられたコンテンツ言語です。
contentType 文字列 このオブジェクトに関連付けられたコンテンツ タイプです。
metadata マップ<文字列, 文字列> デベロッパーがカスタム メタデータで指定した、追加の Key-Value ペアです。

request.resource は、generationmetagenerationetagtimeCreatedupdated を除いて、上記のすべてを含みます。

Cloud Firestore による機能強化

Cloud Firestore 内のドキュメントにアクセスして、他の認証条件を評価できます。

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

以下の例では、特定のクラブのメンバーであるユーザーにファイルへの読み取りアクセスを制限するルールを示しています。

service firebase.storage {
  match /b/{bucket}/o {
    match /users/{club}/files/{fileId} {
      allow read: if club in
        firestore.get(/databases/(default)/documents/users/$(request.auth.id)).memberships
    }
  }
}
次の例では、写真を見ることができるのはユーザーの友だちのみです。
service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/photos/{fileId} {
      allow read: if
        firestore.exists(/databases/(default)/documents/users/$(userId)/friends/$(request.auth.id))
    }
  }
}

これらの Cloud Firestore 関数を使用する最初の Cloud Storage セキュリティ ルールを作成して保存すると、Firebase コンソールまたは Firebase CLI で、2 つのプロダクトを接続する権限を有効にするように求められます。

この機能を無効にするには、Firebase セキュリティ ルールの管理とデプロイの説明に沿って IAM ロールを削除します。

データの検証

Cloud Storage 用の Firebase セキュリティ ルールは、ファイル名とパスの検証、contentTypesize などのファイル メタデータのプロパティの検証といったデータ検証にも使用できます。

service firebase.storage {
  match /b/{bucket}/o {
    match /images/{imageId} {
      // Only allow uploads of any image file that's less than 5MB
      allow write: if request.resource.size < 5 * 1024 * 1024
                   && request.resource.contentType.matches('image/.*');
    }
  }
}

カスタム関数

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

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

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

service firebase.storage {
  match /b/{bucket}/o {
    // 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 /images/{imageId} {
      allow read, write: if signedInOrPublic();
    }
    match /mp3s/{mp3Ids} {
      allow read: if signedInOrPublic();
    }
  }
}

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

次のステップ

条件についての以上の解説により、ルールについての理解を深めることができれば、次の学習に進む準備できています。

コアとなるユースケースの対処方法、およびルールを開発、テスト、デプロイするためのワークフローについて学習する。