Firebase セキュリティ ルールを使用すると、データベースやストレージ バケットに保存されている既存のデータに基づいて、条件付きで新しいデータを書き込むことができます。データ検証を適用する書き込みルールを作成することもできます。その場合は、書き込まれる新しいデータに基づいて書き込みを制限します。ここでは、セキュリティ条件を作成するために既存のデータを使用するルールについて詳しく説明します。
各セクションで、データ検証ルールの詳細を確認するプロダクトを選択してください。
新しいデータに対する制限
Cloud Firestore
特定のフィールドを含むドキュメントが作成されないようにするには、そのフィールドを allow
条件に含めます。たとえば、ranking
フィールドを含むドキュメントの作成を拒否する場合は、次のように、create
条件でこのフィールドを禁止します。
service cloud.firestore {
match /databases/{database}/documents {
// Disallow
match /cities/{city} {
allow create: if !("ranking" in request.resource.data)
}
}
}
Realtime Database
特定の値を含むデータがデータベースに追加されないようにするには、その値をルールに含めて書き込みを禁止します。たとえば、ranking
値を含む書き込みを拒否する場合は、次のように、ranking
値を持つドキュメントへの書き込みを禁止します。
{
"rules": {
// Write is allowed for all paths
".write": true,
// Allows writes only if new data doesn't include a `ranking` child value
".validate": "!newData.hasChild('ranking')
}
}
Cloud Storage
特定のメタデータを含むファイルが作成されないようにするには、そのメタデータを allow
条件に含めます。たとえば、ranking
メタデータを含むファイルの作成を拒否する場合は、create
条件でこのメタデータを禁止します。
service firebase.storage {
match /b/{bucket}/o {
match /files/{allFiles=**} {
// Disallow
allow create: if !("ranking" in request.resource.metadata)
}
}
}
Firebase セキュリティ ルールで既存のデータを使用する
Cloud Firestore
多くのアプリでは、アクセス制御情報をデータベース内のドキュメントのフィールドとして格納します。Cloud Firestore セキュリティ ルールでは、ドキュメント データに基づいて動的にアクセスを許可または拒否できます。
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to read data if the document has the 'visibility'
// field set to 'public'
match /cities/{city} {
allow read: if resource.data.visibility == 'public';
}
}
}
resource
変数は要求されたドキュメントを参照します。resource.data
はドキュメントに格納されているすべてのフィールドと値のマップです。resource
変数の詳細については、リファレンス ドキュメントをご覧ください。
データを書き込むときに、受信データと既存のデータを比較できます。これにより、フィールドが変更されていないことや、フィールドが 1 ずつ増加されていること、新しい値が 1 週間以上先のものであることなどを確実にできます。このケースでルールセットが保留中の書き込みを許可する場合、request.resource
変数にはドキュメントの将来の状態が含まれます。ドキュメント フィールドのサブセットのみを変更する update
オペレーションの場合、request.resource
変数にはオペレーション後の保留中のドキュメントの状態が含まれます。request.resource
のフィールド値をチェックすることで、望ましくない、または整合性のないデータの更新を防ぐことができます。
service cloud.firestore {
match /databases/{database}/documents {
// Make sure all cities have a positive population and
// the name is not changed
match /cities/{city} {
allow update: if request.resource.data.population > 0
&& request.resource.data.name == resource.data.name;
}
}
}
Realtime Database
Realtime Database でデータ構造を適用してデータのフォーマットとコンテンツを検証するには、.validate
ルールを使用します。.validate
ルールによるアクセス権の付与を確認した後、.write
ルールを適用します。
.validate
ルールはカスケード処理されません。ルール内のいずれかのパスまたはサブパスに対する検証ルールが 1 つでも失敗すると、書き込みオペレーション全体が拒否されます。さらに、validate は null 以外の値の確認のみを定義し、以降のデータ削除リクエストをすべて無視します。
次の .validate
ルールを見てみましょう。
{
"rules": {
// write is allowed for all paths
".write": true,
"widget": {
// a valid widget must have attributes "color" and "size"
// allows deleting widgets (since .validate is not applied to delete rules)
".validate": "newData.hasChildren(['color', 'size'])",
"size": {
// the value of "size" must be a number between 0 and 99
".validate": "newData.isNumber() &&
newData.val() >= 0 &&
newData.val() <= 99"
},
"color": {
// the value of "color" must exist as a key in our mythical
// /valid_colors/ index
".validate": "root.child('valid_colors/' + newData.val()).exists()"
}
}
}
}
上記のルールでのデータベースに対する書き込みリクエストの結果は次のようになります。
JavaScript
var ref = db.ref("/widget"); // PERMISSION_DENIED: does not have children color and size ref.set('foo'); // PERMISSION DENIED: does not have child color ref.set({size: 22}); // PERMISSION_DENIED: size is not a number ref.set({ size: 'foo', color: 'red' }); // SUCCESS (assuming 'blue' appears in our colors list) ref.set({ size: 21, color: 'blue'}); // If the record already exists and has a color, this will // succeed, otherwise it will fail since newData.hasChildren(['color', 'size']) // will fail to validate ref.child('size').set(99);
Objective-C
FIRDatabaseReference *ref = [[[FIRDatabase database] reference] child: @"widget"]; // PERMISSION_DENIED: does not have children color and size [ref setValue: @"foo"]; // PERMISSION DENIED: does not have child color [ref setValue: @{ @"size": @"foo" }]; // PERMISSION_DENIED: size is not a number [ref setValue: @{ @"size": @"foo", @"color": @"red" }]; // SUCCESS (assuming 'blue' appears in our colors list) [ref setValue: @{ @"size": @21, @"color": @"blue" }]; // If the record already exists and has a color, this will // succeed, otherwise it will fail since newData.hasChildren(['color', 'size']) // will fail to validate [[ref child:@"size"] setValue: @99];
Swift
var ref = FIRDatabase.database().reference().child("widget") // PERMISSION_DENIED: does not have children color and size ref.setValue("foo") // PERMISSION DENIED: does not have child color ref.setValue(["size": "foo"]) // PERMISSION_DENIED: size is not a number ref.setValue(["size": "foo", "color": "red"]) // SUCCESS (assuming 'blue' appears in our colors list) ref.setValue(["size": 21, "color": "blue"]) // If the record already exists and has a color, this will // succeed, otherwise it will fail since newData.hasChildren(['color', 'size']) // will fail to validate ref.child("size").setValue(99);
Java
FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("widget"); // PERMISSION_DENIED: does not have children color and size ref.setValue("foo"); // PERMISSION DENIED: does not have child color ref.child("size").setValue(22); // PERMISSION_DENIED: size is not a number Map<String,Object> map = new HashMap<String, Object>(); map.put("size","foo"); map.put("color","red"); ref.setValue(map); // SUCCESS (assuming 'blue' appears in our colors list) map = new HashMap<String, Object>(); map.put("size", 21); map.put("color","blue"); ref.setValue(map); // If the record already exists and has a color, this will // succeed, otherwise it will fail since newData.hasChildren(['color', 'size']) // will fail to validate ref.child("size").setValue(99);
REST
# PERMISSION_DENIED: does not have children color and size curl -X PUT -d 'foo' \ https://docs-examples.firebaseio.com/rest/securing-data/example.json # PERMISSION DENIED: does not have child color curl -X PUT -d '{"size": 22}' \ https://docs-examples.firebaseio.com/rest/securing-data/example.json # PERMISSION_DENIED: size is not a number curl -X PUT -d '{"size": "foo", "color": "red"}' \ https://docs-examples.firebaseio.com/rest/securing-data/example.json # SUCCESS (assuming 'blue' appears in our colors list) curl -X PUT -d '{"size": 21, "color": "blue"}' \ https://docs-examples.firebaseio.com/rest/securing-data/example.json # If the record already exists and has a color, this will # succeed, otherwise it will fail since newData.hasChildren(['color', 'size']) # will fail to validate curl -X PUT -d '99' \ https://docs-examples.firebaseio.com/rest/securing-data/example/size.json
Cloud Storage
ルールを評価するときに、アップロード、ダウンロード、変更、または削除するファイルのメタデータも評価する必要がある場合があります。メタデータを評価すると複雑で効果的なルールを作成できるので、特定のコンテンツ タイプを持つファイルだけをアップロードしたり、特定のサイズよりも大きなファイルだけを削除したりすることができます。
resource
オブジェクトには、Cloud Storage オブジェクト内で表面化するファイル メタデータに加え、Key-Value ペアが含まれます。read
または write
リクエストでこれらのプロパティを検査することで、データの整合性を確認できます。resource
オブジェクトは、Cloud Storage バケット内の既存のファイルでメタデータをチェックします。
service firebase.storage {
match /b/{bucket}/o {
match /images {
match /{allImages=**} {
// Allow reads if a custom 'visibility' field is set to 'public'
allow read: if resource.metadata.visibility == 'public';
}
}
}
}
write
リクエスト(アップロード、メタデータの更新、削除など)では request.resource
オブジェクトを使用することもできます。request.resource
オブジェクトは、write
が許可される場合に書き込まれることになるファイルからメタデータを取得します。
この 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
}
}
}
}
resource
オブジェクトに含まれるプロパティの一覧については、リファレンス ドキュメントをご覧ください。