セキュリティは、アプリ開発パズルの最も複雑なピースの 1 つです。ほとんどのアプリケーションでは、開発者は、認証 (ユーザーが誰であるか) と承認 (ユーザーができること) を処理するサーバーを構築して実行する必要があります。
Firebase セキュリティ ルールは、中間 (サーバー) レイヤーを削除し、データに直接接続するクライアントに対してパスベースのアクセス許可を指定できるようにします。このガイドを使用して、着信リクエストにルールが適用される方法について詳しく学んでください。
製品を選択して、そのルールの詳細を確認してください。
クラウド ファイアストア
基本構造
Cloud Firestore と Cloud Storage の Firebase セキュリティ ルールは、次の構造と構文を使用します。
service <<name>> {
// Match the resource path.
match <<path>> {
// Allow the request if the following conditions are true.
allow <<methods>> : if <<condition>>
}
}
ルールを構築する際には、次の重要な概念を理解することが重要です。
- Request:
allow
ステートメントで呼び出されるメソッド。これらは実行を許可するメソッドです。標準メソッドは、get
、list
、create
、update
、およびdelete
です。read
およびwrite
の便利なメソッドにより、指定されたデータベースまたはストレージ パスに対する広範な読み取りおよび書き込みアクセスが可能になります。 - パス: URI パスとして表されるデータベースまたはストレージの場所。
- ルール:要求が true と評価された場合に要求を許可する条件を含む
allow
ステートメント。
セキュリティ ルール バージョン 2
2019 年 5 月の時点で、Firebase セキュリティ ルールのバージョン 2 が利用可能になりました。ルールのバージョン 2 では、再帰ワイルドカード{name=**}
の動作が変更されています。コレクション グループ クエリを使用する場合は、バージョン 2 を使用する必要があります。 rules_version = '2';
を作成して、バージョン 2 にオプトインする必要があります。セキュリティ ルールの最初の行:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
一致するパス
すべての match ステートメントは、コレクションではなく、ドキュメントを指す必要があります。 match ステートメントは、 match /cities/SF
のように特定のドキュメントを指すか、またはmatch /cities/{city}
のようにワイルドカードを使用して指定されたパス内の任意のドキュメントを指すことができます。
上記の例では、match ステートメントで{city}
ワイルドカード構文を使用しています。これは、規則が/cities/SF
や/cities/NYC
などのcities
コレクション内のすべてのドキュメントに適用されることを意味します。 match ステートメントのallow
式が評価されると、 city
変数はSF
やNYC
などの都市ドキュメント名に解決されます。
一致するサブコレクション
Cloud Firestore のデータはドキュメントのコレクションに編成され、各ドキュメントはサブコレクションを通じて階層を拡張できます。セキュリティ ルールが階層データとどのように相互作用するかを理解することが重要です。
cities
コレクションの各ドキュメントにlandmarks
サブコレクションが含まれている状況を考えてみましょう。セキュリティ ルールは一致したパスにのみ適用されるため、 cities
コレクションで定義されたアクセス制御はlandmarks
サブコレクションには適用されません。代わりに、サブコレクションへのアクセスを制御する明示的なルールを記述します。
service cloud.firestore {
match /databases/{database}/documents {
match /cities/{city} {
allow read, write: if <condition>;
// Explicitly define rules for the 'landmarks' subcollection
match /landmarks/{landmark} {
allow read, write: if <condition>;
}
}
}
}
match
ステートメントをネストする場合、内側のmatch
ステートメントのパスは常に、外側のmatch
ステートメントのパスに対して相対的です。したがって、次のルールセットは同等です。
service cloud.firestore {
match /databases/{database}/documents {
match /cities/{city} {
match /landmarks/{landmark} {
allow read, write: if <condition>;
}
}
}
}
service cloud.firestore {
match /databases/{database}/documents {
match /cities/{city}/landmarks/{landmark} {
allow read, write: if <condition>;
}
}
}
再帰ワイルドカード
ルールを任意の深い階層に適用する場合は、再帰ワイルドカード構文{name=**}
を使用します。
service cloud.firestore {
match /databases/{database}/documents {
// Matches any document in the cities collection as well as any document
// in a subcollection.
match /cities/{document=**} {
allow read, write: if <condition>;
}
}
}
再帰的なワイルドカード構文を使用すると、ドキュメントが深くネストされたサブコレクションにある場合でも、ワイルドカード変数には一致するパス セグメント全体が含まれます。たとえば、上記のルールは/cities/SF/landmarks/coit_tower
にあるドキュメントに一致し、 document
変数の値はSF/landmarks/coit_tower
になります。
ただし、再帰ワイルドカードの動作はルールのバージョンに依存することに注意してください。
バージョン 1
セキュリティ ルールは、デフォルトでバージョン 1 を使用します。バージョン 1 では、再帰ワイルドカードは 1 つ以上のパス項目に一致します。空のパスには一致しないため、 match /cities/{city}/{document=**}
はサブコレクション内のドキュメントに一致しますが、 cities
コレクション内のドキュメントには一致しません。一方、 match /cities/{document=**}
はサブコレクション内の両方のドキュメントに一致しますcities
コレクションとサブコレクション。
再帰ワイルドカードは、match ステートメントの最後にある必要があります。
バージョン 2
バージョン 2 のセキュリティ ルールでは、再帰ワイルドカードは 0 個以上のパス項目に一致します。 match/cities/{city}/{document=**}
は、すべてのサブコレクション内のドキュメントと、 cities
コレクション内のドキュメントに一致します。
rules_version = '2';
を追加して、バージョン 2 にオプトインする必要があります。あなたのセキュリティルールの一番上に:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Matches any document in the cities collection as well as any document
// in a subcollection.
match /cities/{city}/{document=**} {
allow read, write: if <condition>;
}
}
}
match ステートメントごとに再帰ワイルドカードを最大 1 つ使用できますが、バージョン 2 では、このワイルドカードを match ステートメントの任意の場所に配置できます。例えば:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Matches any document in the songs collection group
match /{path=**}/songs/{song} {
allow read, write: if <condition>;
}
}
}
コレクション グループ クエリを使用する場合は、バージョン 2 を使用する必要があります。コレクション グループ クエリの保護を参照してください。
match ステートメントの重複
ドキュメントが複数のmatch
ステートメントに一致する可能性があります。複数のallow
式がリクエストに一致する場合、いずれかの条件がtrue
であればアクセスが許可されます。
service cloud.firestore {
match /databases/{database}/documents {
// Matches any document in the 'cities' collection.
match /cities/{city} {
allow read, write: if false;
}
// Matches any document in the 'cities' collection or subcollections.
match /cities/{document=**} {
allow read, write: if true;
}
}
}
上記の例では、最初のルールは常にfalse
ですが、2 番目のルールは常にtrue
であるため、 cities
コレクションに対するすべての読み取りと書き込みが許可されます。
セキュリティ ルールの制限
セキュリティ ルールを操作するときは、次の制限に注意してください。
リミット | 詳細 |
---|---|
リクエストあたりのexists() 、 get() 、およびgetAfter() 呼び出しの最大数 |
いずれかの制限を超えると、許可拒否エラーが発生します。 一部のドキュメント アクセス呼び出しはキャッシュされる可能性があり、キャッシュされた呼び出しは制限に対してカウントされません。 |
ネストされたmatch ステートメントの最大深さ | 10 |
ネストされたmatch ステートメントのセット内で許可される、パス セグメント内の最大パス長 | 100 |
ネストされたmatch ステートメントのセット内で許可されるパス キャプチャ変数の最大数 | 20 |
関数呼び出しの最大深度 | 20 |
関数の引数の最大数 | 7 |
関数あたりのlet 変数バインディングの最大数 | 10 |
再帰的または循環的な関数呼び出しの最大数 | 0 (許可されていません) |
リクエストごとに評価される式の最大数 | 1,000 |
ルールセットの最大サイズ | ルールセットは、次の 2 つのサイズ制限に従う必要があります。
|
クラウドストレージ
基本構造
Cloud Firestore と Cloud Storage の Firebase セキュリティ ルールは、次の構造と構文を使用します。
service <<name>> {
// Match the resource path.
match <<path>> {
// Allow the request if the following conditions are true.
allow <<methods>> : if <<condition>>
}
}
ルールを構築する際には、次の重要な概念を理解することが重要です。
- Request:
allow
ステートメントで呼び出されるメソッド。これらは実行を許可するメソッドです。標準メソッドは、get
、list
、create
、update
、およびdelete
です。read
およびwrite
の便利なメソッドにより、指定されたデータベースまたはストレージ パスに対する広範な読み取りおよび書き込みアクセスが可能になります。 - パス: URI パスとして表されるデータベースまたはストレージの場所。
- ルール:要求が true と評価された場合に要求を許可する条件を含む
allow
ステートメント。
一致するパス
Cloud Storage セキュリティ ルールは、Cloud Storage 内のファイルへのアクセスに使用されるファイル パスmatch
します。ルールは正確なパスまたはワイルドカード パスにmatch
させることができ、ルールをネストすることもできます。リクエスト メソッドを許可する一致ルールがない場合、または条件がfalse
と評価される場合、リクエストは拒否されます。
完全一致
// Exact match for "images/profilePhoto.png" match /images/profilePhoto.png { allow write: if <condition>; } // Exact match for "images/croppedProfilePhoto.png" match /images/croppedProfilePhoto.png { allow write: if <other_condition>; }
ネストされた一致
// Partial match for files that start with "images" match /images { // Exact match for "images/profilePhoto.png" match /profilePhoto.png { allow write: if <condition>; } // Exact match for "images/croppedProfilePhoto.png" match /croppedProfilePhoto.png { allow write: if <other_condition>; } }
ワイルドカードの一致
ルールを使用して、ワイルドカードを使用してパターンをmatch
させることもできます。ワイルドカードは、 profilePhoto.png
などの単一の文字列、またはimages/profilePhoto.png
などの複数のパス セグメントを表す名前付き変数です。
{string}
のように、ワイルドカード名を中かっこで囲んでワイルドカードを作成します。 {path=**}
のように、ワイルドカード名に=**
を追加することで、複数セグメントのワイルドカードを宣言できます。
// Partial match for files that start with "images" match /images { // Exact match for "images/*" // e.g. images/profilePhoto.png is matched match /{imageId} { // This rule only matches a single path segment (*) // imageId is a string that contains the specific segment matched allow read: if <condition>; } // Exact match for "images/**" // e.g. images/users/user:12345/profilePhoto.png is matched // images/profilePhoto.png is also matched! match /{allImages=**} { // This rule matches one or more path segments (**) // allImages is a path that contains all segments matched allow read: if <other_condition>; } }
複数のルールがファイルに一致する場合、結果はすべてのルール評価の結果のOR
になります。つまり、ファイルが一致するいずれかのルールがtrue
と評価された場合、結果はtrue
になります。
上記のルールでは、ファイル "images/profilePhoto.png" はcondition
またはother_condition
が true と評価された場合に読み取ることができますが、ファイル "images/users/user:12345/profilePhoto.png" はother_condition
の結果のみの対象となります。 .
ワイルドカード変数は、ファイル名またはパスの承認を提供するmatch
内から参照できます。
// Another way to restrict the name of a file match /images/{imageId} { allow read: if imageId == "profilePhoto.png"; }
Cloud Storage セキュリティ ルールはカスケードしません。ルールは、リクエスト パスが指定されたルールのパスと一致する場合にのみ評価されます。
評価を依頼する
アップロード、ダウンロード、メタデータの変更、削除は、Cloud Storage に送信されたrequest
を使用して評価されます。 request
変数には、要求が実行されているファイル パス、要求が受信された時刻、および要求が書き込みの場合は新しいresource
値が含まれます。 HTTP ヘッダーと認証状態も含まれます。
request
オブジェクトには、 request.auth
オブジェクトにユーザーの一意の ID と Firebase Authentication ペイロードも含まれています。これについては、ドキュメントの認証セクションで詳しく説明します。
request
オブジェクトのプロパティの完全なリストは、以下で入手できます。
財産 | タイプ | 説明 |
---|---|---|
auth | map<文字列, 文字列> | ユーザーがログインすると、ユーザーの一意の ID であるuid と Firebase Authentication JWT クレームのマップであるtoken が提供されます。それ以外の場合はnull になります。 |
params | map<文字列, 文字列> | リクエストのクエリ パラメータを含むマップ。 |
path | 道 | リクエストが実行されているpath 表すパス。 |
resource | map<文字列, 文字列> | write リクエストのみに存在する新しいリソース値。 |
time | タイムスタンプ | リクエストが評価されるサーバー時間を表すタイムスタンプ。 |
リソース評価
ルールを評価するときに、アップロード、ダウンロード、変更、または削除されるファイルのメタデータを評価することもできます。これにより、特定のコンテンツ タイプのファイルのみをアップロードしたり、特定のサイズを超えるファイルのみを削除したりするなどの複雑で強力なルールを作成できます。
Cloud Storage の Firebase セキュリティ ルールは、 resource
オブジェクトでファイル メタデータを提供します。これには、Cloud Storage オブジェクトで表示されるメタデータのキーと値のペアが含まれます。これらのプロパティは、 read
またはwrite
要求で検査して、データの整合性を確保できます。
write
リクエスト (アップロード、メタデータの更新、削除など) では、リクエスト パスに現在存在するファイルのファイル メタデータを含むresource
オブジェクトに加えて、 request.resource
オブジェクトを使用することもできます。これには、書き込みが許可されている場合に書き込まれるファイル メタデータのサブセットが含まれます。これら 2 つの値を使用して、データの整合性を確保したり、ファイルの種類やサイズなどのアプリケーションの制約を適用したりできます。
resource
オブジェクトのプロパティの完全なリストを以下に示します。
財産 | タイプ | 説明 |
---|---|---|
name | ストリング | オブジェクトの完全な名前 |
bucket | ストリング | このオブジェクトが存在するバケットの名前。 |
generation | 整数 | このオブジェクトのGoogle Cloud Storage オブジェクトの世代。 |
metageneration | 整数 | このオブジェクトのGoogle Cloud Storage オブジェクトのメタ世代。 |
size | 整数 | オブジェクトのサイズ (バイト単位)。 |
timeCreated | タイムスタンプ | オブジェクトが作成された時刻を表すタイムスタンプ。 |
updated | タイムスタンプ | オブジェクトが最後に更新された時刻を表すタイムスタンプ。 |
md5Hash | ストリング | オブジェクトの MD5 ハッシュ。 |
crc32c | ストリング | オブジェクトの crc32c ハッシュ。 |
etag | ストリング | このオブジェクトに関連付けられた etag。 |
contentDisposition | ストリング | このオブジェクトに関連付けられたコンテンツの配置。 |
contentEncoding | ストリング | このオブジェクトに関連付けられたコンテンツ エンコーディング。 |
contentLanguage | ストリング | このオブジェクトに関連付けられたコンテンツの言語。 |
contentType | ストリング | このオブジェクトに関連付けられたコンテンツ タイプ。 |
metadata | map<文字列, 文字列> | 開発者が指定した追加のカスタム メタデータのキーと値のペア。 |
request.resource
には、 generation
、 metageneration
、 etag
、 timeCreated
、およびupdated
を除いて、これらすべてが含まれています。
セキュリティ ルールの制限
セキュリティ ルールを操作するときは、次の制限に注意してください。
リミット | 詳細 |
---|---|
リクエストごとのfirestore.exists() およびfirestore.get() 呼び出しの最大数 | 単一ドキュメント リクエストとクエリ リクエストの場合は 2。 この制限を超えると、許可拒否エラーが発生します。 同じドキュメントへのアクセス呼び出しはキャッシュされる場合があり、キャッシュされた呼び出しは制限に対してカウントされません。 |
完全な例
すべてをまとめると、イメージ ストレージ ソリューションのルールの完全な例を作成できます。
service firebase.storage { match /b/{bucket}/o { match /images { // Cascade read to any image type at any path match /{allImages=**} { allow read; } // Allow write files to the path "images/*", subject to the constraints: // 1) File is less than 5MB // 2) Content type is an image // 3) Uploaded content type matches existing content type // 4) File name (stored in imageId wildcard variable) is less than 32 characters match /{imageId} { allow write: if request.resource.size < 5 * 1024 * 1024 && request.resource.contentType.matches('image/.*') && request.resource.contentType == resource.contentType && imageId.size() < 32 } } } }
リアルタイム データベース
基本構造
Realtime Database では、Firebase セキュリティ ルールは、JSON ドキュメントに含まれる JavaScript に似た式で構成されます。
次の構文を使用します。
{
"rules": {
"<<path>>": {
// Allow the request if the condition for each method is true.
".read": <<condition>>,
".write": <<condition>>,
".validate": <<condition>>
}
}
}
ルールには 3 つの基本要素があります。
- パス:データベースの場所。これは、データベースの JSON 構造を反映しています。
- リクエスト:これらは、ルールがアクセスを許可するために使用するメソッドです。
read
およびwrite
ルールは幅広い読み取りおよび書き込みアクセスを許可しますが、validate
ルールは受信データまたは既存のデータに基づいてアクセスを許可するための二次検証として機能します。 - 条件: true と評価された場合にリクエストを許可する条件。
パスへのルールの適用方法
Realtime Database では、ルールはアトミックに適用されます。つまり、上位レベルの親ノードのルールは、より詳細な子ノードのルールをオーバーライドし、より深いノードのルールは親パスへのアクセスを許可できません。親パスの 1 つに対してアクセス権を既に付与している場合、データベース構造のより深いパスでアクセス権を絞り込んだり取り消したりすることはできません。
次のルールを考慮してください。
{ "rules": { "foo": { // allows read to /foo/* ".read": "data.child('baz').val() === true", "bar": { // ignored, since read was allowed already ".read": false } } } }
このセキュリティ構造により、 /foo/
に値がtrue
の子baz
が含まれている場合はいつでも/bar/
を読み取ることができます。 /foo/bar/
の下の".read": false
ルールは、子パスによってアクセスを取り消すことができないため、ここでは効果がありません。
直観的にはわからないかもしれませんが、これはルール言語の強力な部分であり、最小限の労力で非常に複雑なアクセス権限を実装できます。これは、ユーザーベースのセキュリティに特に役立ちます。
ただし、 .validate
ルールはカスケードしません。書き込みを許可するには、すべての検証ルールが階層のすべてのレベルで満たされている必要があります。
さらに、ルールは親パスには適用されないため、要求された場所またはアクセスを許可する親の場所にルールがない場合、読み取りまたは書き込み操作は失敗します。影響を受けるすべての子パスにアクセスできる場合でも、親の場所での読み取りは完全に失敗します。次の構造を検討してください。
{ "rules": { "records": { "rec1": { ".read": true }, "rec2": { ".read": false } } } }
ルールがアトミックに評価されることを理解していないと、 /records/
パスを取得すると rec1 は返されるがrec1
は返されないように思えるかもしれませrec2
。ただし、実際の結果はエラーです。
JavaScript
var db = firebase.database(); db.ref("records").once("value", function(snap) { // success method is not called }, function(err) { // error callback triggered with PERMISSION_DENIED });
Objective-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[_ref child:@"records"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // success block is not called } withCancelBlock:^(NSError * _Nonnull error) { // cancel block triggered with PERMISSION_DENIED }];
迅速
var ref = FIRDatabase.database().reference() ref.child("records").observeSingleEventOfType(.Value, withBlock: { snapshot in // success block is not called }, withCancelBlock: { error in // cancel block triggered with PERMISSION_DENIED })
ジャワ
FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("records"); ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { // success method is not called } @Override public void onCancelled(FirebaseError firebaseError) { // error callback triggered with PERMISSION_DENIED }); });
残り
curl https://docs-examples.firebaseio.com/rest/records/ # response returns a PERMISSION_DENIED error
/records/
での読み取り操作はアトミックであり、 /records/
の下のすべてのデータへのアクセスを許可する読み取りルールがないため、 PERMISSION_DENIED
エラーがスローされます。 Firebase コンソールのセキュリティ シミュレーターでこのルールを評価すると、読み取り操作が拒否されたことがわかります。
Attempt to read /records with auth=Success(null) / /records No .read rule allowed the operation. Read was denied.
/records/
パスへのアクセスを許可する読み取りルールがないため、操作は拒否されましたが、リクエストしたパスにないため、 rec1
のルールは評価されなかったことに注意してください。 rec1
を取得するには、直接アクセスする必要があります。
JavaScript
var db = firebase.database(); db.ref("records/rec1").once("value", function(snap) { // SUCCESS! }, function(err) { // error callback is not called });
Objective-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // SUCCESS! }];
迅速
var ref = FIRDatabase.database().reference() ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in // SUCCESS! })
ジャワ
FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("records/rec1"); ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { // SUCCESS! } @Override public void onCancelled(FirebaseError firebaseError) { // error callback is not called } });
残り
curl https://docs-examples.firebaseio.com/rest/records/rec1 # SUCCESS!
ロケーション変数
Realtime Database ルールは、パス セグメントに一致する$location
変数をサポートしています。パス セグメントの前に$
プレフィックスを使用して、ルールをパスに沿った子ノードに一致させます。
{
"rules": {
"rooms": {
// This rule applies to any child of /rooms/, the key for each room id
// is stored inside $room_id variable for reference
"$room_id": {
"topic": {
// The room's topic can be changed if the room id has "public" in it
".write": "$room_id.contains('public')"
}
}
}
}
}
$variable
を定数パス名と並行して使用することもできます。
{
"rules": {
"widget": {
// a widget can have a title or color attribute
"title": { ".validate": true },
"color": { ".validate": true },
// but no other child paths are allowed
// in this case, $other means any key excluding "title" and "color"
"$other": { ".validate": false }
}
}
}