安全でないルールの回避

このガイドを使って、Firebase Security Rules 構成における一般的な脆弱性を理解し、独自のルールを確認してセキュリティを強化して、デプロイする前に変更内容をテストします。

データが適切に保護されていないという警告を受け取った場合は、ここで説明している一般的な過ちを確認し、脆弱なルールがあれば更新してください。

Firebase Security Rules にアクセスする

既存の Rules を表示するには、Firebase CLI または Firebase コンソールのいずれかを使用します。更新したルールを誤って上書きしないよう、常に同じ手段を使用してルールを編集します。ローカルに定義されたルールが最新の更新を反映しているかどうかわからなくても、Firebase コンソールには常に Firebase Security Rules の最新のデプロイ済みバージョンが表示されます。

Firebase コンソールからルールにアクセスするには、プロジェクトを選択し、[Realtime Database]、[Cloud Firestore]、または [Storage] に移動します。対象のデータベースやストレージ バケットに移動したら、[ルール] をクリックします。

Firebase CLI からルールにアクセスする場合は、firebase.json ファイルに指定されているルールファイルに移動します。

Firebase Security Rules について理解する

Firebase Security Rules は悪意のあるユーザーからデータを保護します。Firebase コンソールでデータベース インスタンスまたは Cloud Storage バケットを作成する際に、すべてのユーザーに対してアクセスを拒否するか(ロックモード)、すべてのユーザーにアクセス権を付与するか(テストモード)を選択できます。開発中はより自由な構成が必要になることがありますが、十分な時間をかけてルールを適切に構成し、データのセキュリティを確保してからアプリを本番環境にデプロイしてください。

アプリを開発しながらルールのさまざまな構成をテストする際は、ローカル Firebase エミュレータのいずれかを使用して、ローカル開発環境でアプリを実行します。

安全でないルールに関する一般的なシナリオ

デフォルトで設定した、またはアプリの開発で最初に操作した Rules は、アプリをデプロイする前に確認して更新する必要があります。以下に示すよくある誤りを回避して、ユーザーのデータを適切に保護してください。

オープン アクセス

Firebase プロジェクトを設定する際、開発中にオープン アクセスを許可するようにルールを設定してしまうことがあります。アプリのユーザーは自分だけだと思いがちですが、アプリをデプロイすると、そのアプリはインターネット上で利用可能な状態になります。ユーザー認証を行っていない場合や、セキュリティ ルールを構成していない場合は、誰でもプロジェクト ID を推測してデータにアクセスし、データを窃取、変更、削除できてしまいます。

非推奨事項: すべてのユーザーに読み取りと書き込みアクセス権を付与する。
// Allow read/write access to all users under any conditions
// Warning: **NEVER** use this ruleset in production; it allows
// anyone to overwrite your entire database.

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;
    }
  }
}
{
  // Allow read/write access to all users under any conditions
  // Warning: **NEVER** use this ruleset in production; it allows
  // anyone to overwrite your entire database.

  "rules": {
    ".read": true,
    ".write": true
  }
}
    
// Anyone can read or write to the bucket, even non-users of your app.
// Because it is shared with App Engine, this will also make
// files uploaded using App Engine public.
// Warning: This rule makes every file in your Cloud Storage bucket accessible to any user.
// Apply caution before using it in production, since it means anyone
// can overwrite all your files.

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write;
    }
  }
}
    
解決策: 読み取りと書き込みのアクセス権を制限するルール。

データ階層に合ったルールを構築します。この非安全性に対する一般的な解決策の一つは、Firebase Authentication によるユーザー単位のセキュリティです。詳しくは、ルールを使用したユーザーの認証をご覧ください。

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow only authenticated content owners access
    match /some_collection/{document} {
      // Allow reads and deletion if the current user owns the existing document
      allow read, delete: if request.auth.uid == resource.data.author_uid;
      // Allow creation if the current user owns the new document
      allow create: if request.auth.uid == request.resource.data.author_uid;
      // Allow updates by the owner, and prevent change of ownership
      allow update: if request.auth.uid == request.resource.data.author_uid
                    && request.auth.uid == resource.data.author_uid;

    }
  }
}
  
service cloud.firestore {
  match /databases/{database}/documents {
    // Allow public read access, but only content owners can write
    match /some_collection/{document} {
      // Allow public reads
      allow read: if true
      // Allow creation if the current user owns the new document
      allow create: if request.auth.uid == request.resource.data.author_uid;
      // Allow updates by the owner, and prevent change of ownership
      allow update: if request.auth.uid == request.resource.data.author_uid
                    && request.auth.uid == resource.data.author_uid;
      // Allow deletion if the current user owns the existing document
      allow delete: if request.auth.uid == resource.data.author_uid;
    }
  }
}
  
{
  "rules": {
    "some_path": {
      "$uid": {
        // Allow only authenticated content owners access to their data
        ".read": "auth !== null && auth.uid === $uid",
        ".write": "auth !== null && auth.uid === $uid"
      }
    }
  }
}
    
{
  // Allow anyone to read data, but only authenticated content owners can
  // make changes to their data

  "rules": {
    "some_path/$uid": {
      ".read": true,
      // or ".read": "auth.uid !== null" for only authenticated users
      ".write": "auth.uid === $uid"
    }
  }
}
    
// Grants a user access to a node matching their user ID
service firebase.storage {
  match /b/{bucket}/o {
    // Files look like: "user/<UID>/file.txt"
    match /user/{userId}/{fileName} {
      allow read, write: if request.auth.uid == userId;
    }
  }
}
service firebase.storage {
  match /b/{bucket}/o {
    // Files look like: "user/<UID>/file.txt"
    match /user/{userId}/{fileName} {
      allow read;
      allow write: if request.auth.uid == userId;
    }
  }
}
  

認証されたユーザーのアクセス

Rules によってユーザーがログイン済みであることが確認されますが、その認証に基づき、それ以上はアクセスが制限されないことがあります。いずれかのルールに auth != null が含まれている場合は、ログインしているユーザーがデータにアクセス可能にする必要があることを確認します。

非推奨事項: ログインしているすべてのユーザーが、データベース全体に対して読み取りと書き込みのアクセス権を持っている。
service cloud.firestore {
  match /databases/{database}/documents {
    match /some_collection/{document} {
      allow read, write: if request.auth.uid != null;
    }
  }
}
{
  "rules": {
    ".read": "auth.uid !== null",
    ".write": "auth.uid !== null"
  }
}
// Only authenticated users can read or write to the bucket
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}
解決策: セキュリティ条件を使用してアクセスを制限する。

認証を確認するときは、いずれかの認証プロパティを使用して、特定のユーザーによる特定のデータセットへのアクセスをさらに制限することもできます。さまざまな認証プロパティの詳細については、こちらをご覧ください。

service cloud.firestore {
  match /databases/{database}/documents {
    // Assign roles to all users and refine access based on user roles
    match /some_collection/{document} {
     allow read: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == "Reader"
     allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == "Writer"

     // Note: Checking for roles in your database using `get` (as in the code
     // above) or `exists` carry standard charges for read operations.
    }
  }
}
// Give each user in your database a particular attribute
// and set it to true/false
// Then, use that attribute to grant access to subsets of data
// For example, an "administrator" attribute set
// to "true" grants write access to data

service cloud.firestore {
  match /databases/{database}/documents {
    match /some_collection/{document} {
      allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
      allow read: true;
    }
  }
}
  
service cloud.firestore {
  match /databases/{database}/documents {
    // Allow public read access, but only content owners can write
    match /some_collection/{document} {
      allow read: if true
      allow write: if request.auth.uid == request.resource.data.author_uid
    }
  }
}
  
{
  "rules": {
    "some_path": {
      "$uid": {
        // Allow only authenticated content owners access to their data
        ".read": "auth.uid === $uid",
        ".write": "auth.uid === $uid"
      }
    }
  }
}
    
{
  "rules": {
    "some_path/$uid": {
      ".write": "auth.uid === $uid",
      // Create a "public" subpath in your dataset
      "public": {
        ".read": true
        // or ".read": "auth.uid !== null"
      },
      // Create a "private" subpath in your dataset
      "private": {
        ".read": "auth.uid === $uid"
      }
    }
  }
}
    
{
  // Allow anyone to read data, but only authenticated content owners can
  // make changes to their data

  "rules": {
    "some_path/$uid": {
      ".read": true,
      // or ".read": "auth.uid !== null" for only authenticated users
      ".write": "auth.uid === $uid"
    }
  }
}
    
// Allow reads if the group ID in your token matches the file metadata `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;
}
// Grants a user access to a node matching their user ID
service firebase.storage {
  match /b/{bucket}/o {
    // Files look like: "user/<UID>/file.txt"
    match /user/{userId}/{fileName} {
      allow read, write: if request.auth.uid == userId;
    }
  }
}
service firebase.storage {
  match /b/{bucket}/o {
    // Files look like: "user/<UID>/file.txt"
    match /user/{userId}/{fileName} {
      allow read;
      allow write: if request.auth.uid == userId;
    }
  }
}
  

Realtime Database)ルールの不適切な継承

Realtime Database Security Rules は、より狭いルールで、より深い子ノードのルールを上書きする親パスをカスケードします。子ノードでルールを書き込むときは、追加の権限しか付与できないことに注意してください。データベースのより深いパスのデータへのアクセスを絞り込んだり取り消したりすることはできません。

非推奨事項: 子パスでのルールの絞り込み
{
  "rules": {
     "foo": {
        // allows read to /foo/*
        ".read": "data.child('baz').val() === true",
        "bar": {
          /* ignored, since read was allowed already */
          ".read": false
        }
     }
  }
}
解決策: 広いルールを親パスで作成し、子パスでより詳細な権限を付与する。より細かくデータアクセスを制限する場合は、ルールも細かくします。Realtime Database Security Rules のカスケードの詳細については、Realtime Database Security Rules のコア構文をご覧ください。

クローズド アクセス

アプリの開発中における一般的なアプローチとしては、データを遮断するという方法もあります。通常、これは以下のように、すべてのユーザーに対して読み取りと書き取りのアクセス権を拒否することを意味します。

// Deny read/write access to all users under any conditions
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if false;
    }
  }
}
{
  "rules": {
    ".read": false,
    ".write": false
  }
}
    
// Access to files through Cloud Storage is completely disallowed.
// Files may still be accessible through App Engine or Google Cloud Storage APIs.

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if false;
    }
  }
}

Firebase Admin SDK と Cloud Functions は、引き続きデータベースにアクセスできます。Firebase Admin SDK と組み合わせ、サーバーのみのバックエンドとして Cloud Firestore または Realtime Database を使用する場合は、これらのルールを使用します。これによってセキュリティは確保されますが、アプリのクライアントによって適切にデータが取得されることをテストする必要が生じます。

Cloud Firestore Security Rules とその仕組の詳細については、Cloud Firestore Security Rules を使ってみるをご覧ください。

Cloud Firestore Security Rules をテストする

アプリの動作確認と Cloud Firestore Security Rules 構成の検証を行うには、Firebase エミュレータを使用します。変更をデプロイする前に、Cloud Firestore エミュレータを使用して、ローカル環境で単体テストの実施と自動化を行います。

Firebase コンソールで Firebase Security Rules をすばやく検証するには、Firebase ルール シミュレータを使用します。