見出し画像

Firebaseセキュリティルール

「Firebase」の「Firebaseセキュリティルール」についてまとめました。

1. Firebaseセキュリティルール

「Firebase」では、「Firebase セキュリティルール」を使用して、「Cloud Firestore」「Cloud Storage」のデータを保護します。ユーザーがアクセスできるデータを定義します。

2. 本番モードとテストモード

「Cloud Firestore」「Cloud Storage」ではストレージ構築時にセキュリティルールとして、「本番モード」と「テストモード」の選択を要求されます。

「本番モード」は全ユーザーアクセス不可、「テストモード」は全ユーザーアクセス可となります。

・本番モード - 全ユーザーアクセス不可

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

・テストモード - 全ユーザーアクセス可 (1ヶ月間)

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.time < timestamp.date(2023, 4, 1);
    }
  }
}


「Firebase Authentication」で認証したユーザーのみアクセス可にするには、次のように設定します。

・Firebase Authenticationで認証したユーザーのみアクセス可

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

本番運用では、データごとに自分のデータにしかアクセスできないようにするなど、サービスに適したルールを設定する必要があります。

3. Firebaseセキュリティルールの書式

3-1. 基本書式

「セキュリティルール言語」の基本書式は、次のとおりです。

rules_version = '2';
service <<name>> {
  // リソースパスのマッチング
  match <<path>> {
    // 条件がtrueの場合に要求を許可
    allow <<methods>> : if <<condition>>
  }
}

・rules_version : 構文のバージョン (v2)
・service
: ルールが適用されるサービス (firebase.storage, cloud.firestore)
・match : ルールが適用されるストレージのパス
・allow : trueと評価された場合に要求を許可
    ・methods : メソッド (get, list, create, update, delete)
    ・condition : コンディション

3-2. match

「match」には、次の2種類があります。

Partial matches :  request.pathのプレフィックス一致
Complete matches :  request.pathの全体一致

「request.path」に「/example/hello/nested/path」を渡すと、次のように許可するかどうかを判定します。{variable} はシングルセグメントワイルドカード、{variable=**} はマルチセグメントワイルドカードとなります。

service firebase.storage {
  // Partial match
  match /example/{singleSegment} {   // singleSegment == 'hello'
    allow write;                     // writeを不許可
    // Complete match.
    match /nested/path {             // singleSegment visible in scope.
      allow read;                    // readを許可
    }
  }
  // Complete match
  match /example/{multiSegment=**} { // multiSegment == /hello/nested/path
    allow read;                      // readを許可
  }
}

3-3. allow

「allow」で許可するかどうかは、一致したすべてのルールの論理和になります。次の例では、2つ目のルールでread・deleteを許可していませんが、1つ目のルールでread・deleteを許可してるため、許可となります。

service firebase.storage {
  // userディレクトリ下のデータのread・deleteを許可
  match /users/{userId}/{anyUserFile=**} {
    allow read, delete: if request.auth != null && request.auth.uid == userId;
  }

  // userディレクトリ下のpngのread・delete・writeを許可
  match /users/{userId}/images/{imageId} {
    allow write: if request.auth != null && request.auth.uid == userId && imageId.matches('*.png');
  }
}

3-4. methods

「allow」に指定する「methods」は、次のとおりです。

・Convenience methods
    ・read
    ・write
・Standard methods
    ・get
    ・list
    ・update
    ・delete

同じmatch内でのreadやwriteのオーバーラップはエラーとなります。たとえば、次のルールはエラーとなります。

service bad.example {
  match /rules/with/overlapping/methods {
    // 認証済みユーザーのreadを許可
    allow read: if request.auth != null;

    match another/subpath {
      // readのオーバーラップでエラー
      allow get: if request.auth != null && request.auth.uid == "me";

      // writeのオーバーラップでエラー
      allow write: if request.auth != null;
      allow create: if request.auth != null && request.auth.uid == "me";
    }
  }
}

3-5. function

セキュリティルールは、カスタム関数もサポートしています。JavaScriptに似ていますが、ドメイン固有の言語で記述されており、いくつかの重要な制限があります。

・関数には1つのreturnのみを含めることが可能。
ループや外部サービスの呼び出しはできません。
・関数は定義されているスコープの関数や変数にアクセス可能。
例えばcloud.firestoreで定義された関数は、リソース変数、get()、exists()などの組み込み関数にアクセスできます。
・関数は他の関数を呼び出すことは可能だが再帰は不可。
呼び出しスタックの深さは最大20です。
・letを使って変数を定義することが可能。
最大10個持つことができますが、returnで終了しなければなりません。

service cloud.firestore {
  match /databases/{database}/documents {
    // 認証済みユーザーか、データが「public」である場合はtrue
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }

    match /cities/{city} {
      allow read, write: if signedInOrPublic();
    }

    match /users/{user} {
      allow read, write: if signedInOrPublic();
    }
  }
}

let代入文は「;」で区切る必要があります。

function isAuthorOrAdmin(userId, article) {
  let isAuthor = article.author == userId;
  let isAdmin = exists(/databases/$(database)/documents/admins/$(userId));
  return isAuthor || isAdmin;
}

データアクセスには遅延が発生します。以下では、isAuthorがtrueの場合のみ2番目の関数を呼び出して対策しています。

function isAdmin(userId) {
  return exists(/databases/$(database)/documents/admins/$(userId));
}
function isAuthorOrAdmin(userId, article) {
  let isAuthor = article.author == userId;
  return isAuthor || isAdmin(userId);
}

4. Firebaseセキュリティルールの変数

4-1. request

「request」は、データアクセスのリクエストの情報を、ルールで利用するための変数です。

・request.auth : Firebase Authentication からの認証資格情報を含むJSON Web トークン (JWT)
・request.method : メソッド
・request.params : request.resourceに関連しないデータ
・request.path : ターゲットリソースのパス

4-2. resource

「resource」は、データアクセスをリクエストされているデータの情報を、ルールで利用するための変数です。

キーと値のペアのマップで表されるサービス内の現在の値です。条件内でリソースを参照すると、サービスから値が最大 1 回読み取られます。このルックアップは、リソースのサービス関連のクォータにカウントされます。

4-3. auth

「auth」はデータアクセスをリクエストしているユーザーの情報を、ルールで利用するための変数です。

・auth.uid : ユーザーID
・auth.token : Authenticationによって収集された値のマップ

auth.tokenには、次の値が含まれます。

・email : メールアドレス
・email_verified : ユーザーにメールアドレスへのアクセス権があるかどうか
・phone_number : 電話番号
・name : ユーザーの表示名
・sub : Firebase UID (プロジェクト内で一意)
・firebase.identities : このユーザーのアカウントに関連付けられている IDの辞書 (email, phone, google.com, facebook.com, github.com, twitter.com)
・firebase.sign_in_provider : ログインプロバイダ (custom, password, phone, anonymous, google.com, facebook.com, github.com, twitter.com)
・firebase.tenant : tenantId

関連



この記事が気に入ったらサポートをしてみませんか?