Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

基本的なセキュリティ ルール

Firebase セキュリティ ルールを使用すると、保存データへのアクセスを制御できます。ルールの構文は柔軟なので、データベース全体に対するすべての書き込みオペレーションから特定のドキュメントに対するオペレーションまで、あらゆるオペレーションに適格なルールを作成できます。

このガイドでは、アプリを設定してデータを保護する際に実装する、いくつかの基本的なユースケースについて説明します。ただし、ルールを作成するための言語とルールの動作についての詳細を確認してからルールの作成を始めることをおすすめします。

作成したルールにアクセスして更新するには、Firebase セキュリティ ルールの管理とデプロイで説明されている手順に従ってください。

デフォルト ルール: ロックモード

Firebase コンソールでデータベース インスタンスやストレージ インスタンスを作成するときに、Firebase セキュリティ ルールでデータへのアクセスを制限するか(ロックモード)、すべてのユーザーにデータへのアクセスを許可するか(テストモード)を選択します。Cloud Firestore と Realtime Database の場合、ロックモードでのデフォルト ルールは、すべてのユーザーに対してアクセスを拒否します。Cloud Storage の場合は、認証されたユーザーのみが Storage バケットにアクセスできます。

Cloud Firestore

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if false;
    }
  }
}

Realtime Database

{
  "rules": {
    ".read": false,
    ".write": false
  }
}

Cloud Storage

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

開発環境でのルール

アプリの開発中は、比較的自由に、あるいは制限なしでデータにアクセスする必要がある場合があります。いずれにしても、必ずセキュリティ ルールを更新してからアプリを本番環境にデプロイしてください。また、アプリをデプロイしたら、まだリリースしていない場合でも一般公開されることを念頭に置いてください。

Firebase ではクライアントがデータに直接アクセスできるため、悪意のあるユーザーに対してアクセスをブロックしてデータを保護するには、Firebase セキュリティ ルールが唯一の手段となります。プロダクトのロジックとは別にルールを定義する利点は多くあります。クライアントがセキュリティ実施の責任を担う必要がなくなる点、バグのある実装によりデータを危険にさらすことがなくなる点が挙げられます。そして最も重要な利点は、中間サーバーに依存することなく、外部からデータを保護できることです。

認証済みのすべてのユーザー

ログイン済みのユーザーであれば誰でもデータにアクセスできる状態のままにすることはおすすめしませんが、アプリの開発中は、認証済みのすべてのユーザーがデータにアクセスできるように設定すると便利です。

Cloud Firestore

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.auth != null;
    }
  }
}

Realtime Database

{
  "rules": {
    ".read": "auth.uid != null",
    ".write": "auth.uid != null"
  }
}

Cloud Storage

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

本番環境対応のルール

アプリのデプロイ準備を進める際は、データが保護されていること、データへのアクセス権が適切にユーザーに付与されていることを確認します。ユーザーベースのアクセス権を設定するには、Authentication を活用します。また、データベースから直接データを読み取ってデータに基づくアクセス権を設定します。

データを構造化する際にルールの作成を行うことを検討してください。ルールをどう設定するかにより、異なるパスのデータへのアクセスを制限する方法が変わってくるためです。

コンテンツ所有者のみのアクセス

これは、認証済みのコンテンツ所有者にのみアクセスを制限するというルールです。データの読み取りや書き込みを行うことができるのは 1 人のユーザーに限られます。そのユーザーの ID がデータパスに含まれています。

このルールが機能するケース: このルールが適切に機能するのは、ユーザーによってデータがサイロ化されている場合です。つまり、データにアクセスする必要がある唯一のユーザーが、そのデータを作成した本人である場合です。

このルールが機能しないケース: このルールセットが機能しないのは、同じデータに対して複数のユーザーが書き込みや読み取りを行う必要がある場合です。つまり、複数のユーザーがデータを上書きしようとする場合、ユーザー自身が作成したデータでもそれにアクセスできなくなってしまいます。

このルールを設定する: データの読み取りや書き込みへのアクセスをリクエストしているユーザーがそのデータの所有者であることを確認するルールを作成します。

Cloud Firestore

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow only authenticated content owners access
    match /some_collection/{userId}/{documents=**} {
      allow read, write: if request.auth != null && request.auth.uid == userId
    }
  }
}

Realtime Database

{
  "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"
      }
    }
  }
}

Cloud Storage

// Grants a user access to a node matching their user ID
service firebase.storage {
  match /b/{bucket}/o {
    // Files look like: "user/<UID>/path/to/file.txt"
    match /user/{userId}/{allPaths=**} {
      allow read, write: if request.auth != null && request.auth.uid == userId;
    }
  }
}

公開アクセスとプライベート アクセスの混合

このルールでは、すべてのユーザーにデータセットの読み取りが許可されますが、指定されたパスのデータの作成や変更ができるのは、認証済みのコンテンツ所有者のみとなります。

このルールが機能するケース: このルールが適切に機能するのは、アプリで一般公開で読み取り可能にする必要のある要素が存在する一方、こうした要素への編集権限をその所有者のみに制限しなければならない場合です。このようなアプリの例としては、チャットアプリやブログが挙げられます。

このルールが機能しないケース: このルールセットが機能しないのは、コンテンツ所有者のみのアクセスルールと同様、同じデータに対して複数のユーザーが編集する必要のある場合です。最終的に、複数のユーザーが互いのデータを上書きすることになってしまいます。

このルールを設定する: すべてのユーザー(または認証済みのすべてのユーザー)に対して読み取りアクセスを有効にし、データの書き込みを行うユーザーがそのデータの所有者であることを確認するルールを作成します。

Cloud Firestore

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 create: if request.auth.uid == request.resource.data.author_uid;
      allow update, delete: if request.auth.uid == resource.data.author_uid;
    }
  }
}

Realtime Database

{
// 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"
      }
    }
  }
}

Cloud Storage

service firebase.storage {
  match /b/{bucket}/o {
    // Files look like: "user/<UID>/path/to/file.txt"
    match /user/{userId}/{allPaths=**} {
      allow read;
      allow write: if request.auth.uid == userId;
    }
  }
}

属性ベースのアクセスとロールベースのアクセス

これらのルールを機能させるには、データ内に属性を定義して、その属性をユーザーに割り当てる必要があります。Firebase セキュリティ ルールはリクエストをデータベース内のデータまたはファイルのメタデータと照合して、アクセスを確認または拒否します。

このルールが機能するケース: ユーザーにロールを割り当てている場合、このルールを使用すると、ロールや特定のユーザー グループに基づいて簡単にアクセスを制限できます。たとえば、成績を保存する場合、「学生」グループ、「教師」グループ、「校長」グループにそれぞれ異なるアクセスレベルを割り当て、学生には自分のコンテンツの読み取りのみ、教師には担当学科の読み取りと書き込み、校長にはすべてのコンテンツの読み取りを許可します。

このルールが機能しないケース: Cloud Firestore のルールには get() メソッドを組み込めますが、Realtime Database と Cloud Storage のルールでは、このメソッドを利用できません。したがって、ルールで使用している属性を反映させるためにデータベースやファイル メタデータを構造化する必要があります。

このルールを設定する: Cloud Firestore では、読み取り可能なユーザーのドキュメント内でフィールドを含めてから、そのフィールドを読み取って条件付きでアクセス権を付与するようにルールを構造化します。Realtime Database では、アプリのユーザーを定義するデータパスを作成し、子ノードでそれらのユーザーにロールを付与します。

あるいは、Authentication でカスタム クレームを設定するという方法もあります。この場合、カスタム クレームの情報を任意の Firebase セキュリティ ルール内の auth.token 変数から取得できます。

データ定義の属性とロール

これらのルールは Cloud Firestore と Realtime Database でのみ機能します。

Cloud Firestore

次のようにルールに read を含める場合は例外なく、Cloud Firestore での読み取りオペレーションが課金対象となります。

service cloud.firestore {
  match /databases/{database}/documents {
    // For attribute-based access control, Check a boolean `admin` attribute
    allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
    allow read: true;

    // Alterntatively, for role-based access, assign specific roles to users
    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"
   }
  }
}

Realtime Database

{
  "rules": {
    "some_path": {
      "${subpath}": {
        //
        ".write": "root.child('users').child(auth.uid).child('role').val() == 'admin'",
        ".read": true
      }
    }
  }
}

カスタム クレームの属性とロール

これらのルールを実装するには、Firebase Authentication でカスタム クレームを設定し、そのクレームをルール内で利用します。

Cloud Firestore

次のようにルールに read を含める場合は例外なく、Cloud Firestore での読み取りオペレーションが課金対象となります。

service cloud.firestore {
  match /databases/{database}/documents {
    // For attribute-based access control, check for an admin claim
    allow write: if request.auth.token.admin == true;
    allow read: true;

    // Alterntatively, for role-based access, assign specific roles to users
    match /some_collection/{document} {
     allow read: if request.auth.token.reader == "true";
     allow write: if request.auth.token.writer == "true";
   }
  }
}

Realtime Database

{
  "rules": {
    "some_path": {
      "$uid": {
        // Create a custom claim for each role or group
        // you want to leverage
        ".write": "auth.uid != null && auth.token.writer == true",
        ".read": "auth.uid != null && auth.token.reader == true"
      }
    }
  }
}

Cloud Storage

service firebase.storage {
  // 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;
  }
}