LoginSignup
46
30

More than 5 years have passed since last update.

Firestoreでチャットルームを作る時のSecurity Rulesを考えてみた。

Posted at

Firestoreでチャットルームを作る時のSecurity Rulesを考えてみた。

Firebaseの新しいサービスFirestoreを使って、チャットルームを作成する時のSecurity Rulesを検討してみました。

前提として、以下のようなチャットルームを想定。

  • 1対1
  • 書き込みは削除できない
  • 未読/既読の状態を持つ

Security Rulesのベースは、Firestoreマニュアルに参考になる記載があり、今回は、それを発展させて検討しました。

検討結果のSecurity Rules.

service cloud.firestore {
  match /databases/{database}/documents {
    allow read, write: if false;
    match /users/{userId} {
      allow read, write: if request.auth.uid == userId;
      match /rooms/{roomId} {
        allow read, write: if request.auth.uid == userId;
      }
    }
    match /rooms/{roomId} {
      allow read, write: if exists(/databases/$(database)/documents/users/$(request.auth.uid)/rooms/$(roomId));
      match /messages/{messageId} {
        allow read: if exists(/databases/$(database)/documents/users/$(request.auth.uid)/rooms/$(roomId));
        allow create: if exists(/databases/$(database)/documents/users/$(request.auth.uid)/rooms/$(roomId))
                             && request.resource.data.keys().hasAll(["datetime", "content", "userId"])
                             && request.resource.data.size() == 3
                             && request.resource.data.datetime is timestamp
                             && request.time < request.resource.data.datetime + duration.value(1, 'm')
                             && request.resource.data.content is string
                             && request.resource.data.userId is string;
        allow update: if exists(/databases/$(database)/documents/users/$(request.auth.uid)/rooms/$(roomId))
                             && request.resource.data.keys().hasAll(["read"])
                             && request.resource.data.read is bool
                             && resource.data.datetime == request.resource.data.datetime
                             && resource.data.content == request.resource.data.content
                             && resource.data.userId == request.resource.data.userId
                             && request.resource.data.size() == 4;
      }
    }
  }
}

ユーザ毎に自身のチャットルーム(rooms)のID(roomId)を持ち、チャットルーム毎にメッセージのcollectionを持つ構成は、マニュアルに記載されている通り。
追加のRuleとして、メッセージとして格納する情報を厳密に評価することで、セキュリティを確保しています。

メッセージ追加時のルール

        allow create: if exists(/databases/$(database)/documents/users/$(request.auth.uid)/rooms/$(roomId))
                             && request.resource.data.keys().hasAll(["datetime", "content", "userId"])
                             && request.resource.data.size() == 3
                             && request.resource.data.datetime is timestamp
                             && request.time < request.resource.data.datetime + duration.value(1, 'm')
                             && request.resource.data.content is string
                             && request.resource.data.userId is string;

メッセージ追加(allow create)で設定している評価

  • 追加するフィールドを明確にしてkey名称の評価
  • 追加するデータのsizeの評価
  • データのvalueの型の評価
  • 追加されるメッセージの時間評価 作成されるデータに時間の不正が行われないよう現在日時から1分以内の追加であるかどうかを評価(request.time < request.resource.data.datetime + duration.value(1, 'm'))

メッセージ既読処理時のルール

        allow update: if exists(/databases/$(database)/documents/users/$(request.auth.uid)/rooms/$(roomId))
                             && request.resource.data.keys().hasAll(["read"])
                             && request.resource.data.read is bool
                             && resource.data.datetime == request.resource.data.datetime
                             && resource.data.content == request.resource.data.content
                             && resource.data.userId == request.resource.data.userId
                             && request.resource.data.size() == 4;

メッセージ既読処理時(allow update)で設定している評価

既読処理として、未読/既読を制御するフラグ(read)しか書き換えられないようにします。

  • updateされる情報のkeyとして未読/既読を制御するフラグ(read)が含まれている
  • フラグ(read)のvalueの型の評価
  • 他のFieldが追加されていないことを確認する為のsizeの評価
  • データが書き換えられていないことを確認する為の既存データとupdateされるデータでのフラグ以外のデータの一致の評価

ハマりやすそうな注意点として、update処理では、 readのみ追加しても、rule時には、data.size()は、ドキュメントのデータサイズになり、request.dataに書き換えていないデータが含まれるところでしょうか。

補足

メッセージを投稿したユーザだけ削除可能にする評価

match /messages/{messageId} 配下に以下を追加します.

       allow delete: if exists(/databases/$(database)/documents/users/$(request.auth.uid)/rooms/$(roomId))
                                               && resource.data.userId == request.auth.uid; 
46
30
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
46
30