【Firebase】Realtime Databaseのルール設定方法!Authのログイン識別
この記事からわかること
- FirebaseのRealtime Databaseでセキュリティルールを設定する方法
- 読み取り/書き込みの指定方法
- Authentication(アカウント認証)によるルール設定
- 検証やデータベースのキーを指定するには?
- 組み込み変数や$location変数、関数の使い方
- 実際に使用する設定例
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
参考文献:FirebaseRealtimeデータベースのセキュリティルールを理解する
FirebaseのRealtime Databaseでユーザーに対してデータの読み取りや書き込みを制限するルールを設定する方法をまとめていきます。
セキュリティルール
Realtime Databaseではデータベースへのアクセス権を独自のセキュリティルールを定義して管理しています。これにより適切なユーザーのみにデータを提供でき、悪質なデータベースの操作や情報の抜き取りから保護されるようにななっています。
セキュリティルールはRealtime Databaseのプロジェクト内から設定でき、JSON形式で記述されています。またここではルールの編集や許可や却下の監視、デモデータを使ったアクセス権のシミュレーションが行えます。
データベース作成時にルールのモードを設定でき、本番環境の場合はロックモードに、テスト環境の場合はテストモードを指定し、指定したモードによってデフォルトのルールが設定されます。
ロックモードの場合は全てのユーザーが読み取り/書き込みができない条件に、テストモードの場合は全て許可された条件になります。
ロックモード
{
"rules": {
".read": false,
".write": false
}
}
テストモード
{
"rules": {
".read": true,
".write": true
}
}
ルールの種類
ルールの指定に使用するのは主に4種類です。これらをJSONのキーとして設定し値に格納されているデータによってアクセス権が分岐します。
- .read:読み取り権
- .write:書き込み権
- .validate:検証
- .indexOn:インデックス
readとwriteには真偽値を渡すことで許可/却下を識別します。
indexOnはクエリパフォーマンスを向上させるために使用します。
ルールの記述方法
大枠はrulesをキーとする連想配列になっており、その中にさまざまな条件を記述していきます。値に格納するのは真偽値だけでなく条件式を組み込むことも可能です。
例えばデータベースの中のパス/users/
のデータに対して読み取りのみ制限をなくすには以下のようにします。
{
"rules": {
"users": {
".read": true,
".write": false
}
}
}
条件は指定したパスより配下にあるデータに対しても適応されるようです。この場合は/users/id/list/item
などがあっても読み取りしかできなくなります。
パスのキーを$location変数に格納
データベースに指定しているキーは$location変数と呼ばれる変数を組み込むことでルール内で扱うことができるようになります。変数への格納方法は$をつけた任意の文字列を指定の階層のキーに記述するだけです。これにより配下の階層で$変数名で対象のキー値を参照することができるようになります。authについては後述しますがここではusers/<各ユーザーID>/list
の<各ユーザーID>
を$location変数$uid
として使用できるようにしています。
{
"rules": {
"users": {
"$uid": {
".write": "$uid === auth.uid"
}
}
}
}
組み込み変数や関数
セキュリティルールに記述できるのは真偽値だけでなく条件式(&&など)や用意された組み込み変数、関数などを使用できます。組み込み変数から新規追加される値を取得などが、関数からデータ型を識別したりすることが可能です。
組み込み変数
変数 | 概要 |
---|---|
now | 現在時刻 |
data | 書き込み前のデータ |
newData | 書き込みされたデータ |
root | データベースのルート |
dataやrootからはRuleDataSnapshot型のデータを取得でき、用意されているプロパティやメソッドを使用できます。
メソッド(関数)
関数 | 概要 |
---|---|
isString() | 文字列かどうか |
isNumber() | 数値かどうか |
isBoolean() | 真偽値かどうか |
exists() | 存在するかどうか |
hasChild('子パス') | 子要素が存在するかどうか |
hasChildren(['子パス']) | 複数の子要素が存在するかどうか |
val() | 値の参照 |
child('パス名') | 小ノードを参照 |
parent() | 親ノードを参照 |
matches() | 正規表現にマッチするかどうか |
参考文献:FirebaseデータベースセキュリティルールAPI
Authenticationとの連携
Firebaseが提供している認証機能Authenticationとも連携した条件を指定することも可能です。変数auth
を使用することで認証情報に参照することができます。もちろんFirebaseプロジェクトにAuthenticationを導入している必要があります。
未認証であれば変数auth
にはnull
が格納されます。
認証時のみ全て許可
{
"rules": {
"users": {
".read": "auth != null",
".write": "auth != null"
}
}
}
$location変数を利用することでログインしているIDと同じキーを持つデータのみ書き込みを許可することも可能です。
users/<各ユーザーID>と認証アカウントIDが一致するかどうか
{
"rules": {
"users": {
"$uid": {
".write": "$uid === auth.uid"
}
}
}
}
格納するデータを検証する
validate
は格納できるデータを制限する時などに使用できます。検証ルールは配下の階層には適応されず、指定した階層のみに適応されます。
文字列型かつ文字数20文字以内
{
"rules": {
"comment": {
".validate": "newData.isString() && newData.val().length < 20"
}
}
}
実際に使用する設定例
実際に使用するルール設定をまとめてみました。以下のようなデータベース構造を構築したとして考えていきます。
"users": {
"<user_id>": {
"group": {
"<group_id>" : {
"<item_id>" : "itemName1",
"<item_id>" : "itemName2"
},
},
"member" : {
"<user_id>" : "userName1",
"<user_id>" : "userName2"
},
"shareHost": {
"<user_id>" : "userName"
},
"userName": "userName"
}
}
usersの中にユーザーIDごとにまとめられたユーザー情報と作成したグループが管理されています。 また今回はデータを共有できるようにするためにホストとメンバーという関係を構築しています。
ユーザーはホストかメンバーどちらかになり得る可能性がありホスト側にはメンバーデータ(member)がメンバー側にはホストデータ(shareHost)を保持させます。
全員不可
{
"rules": {
".read": false,
".write": false
}
}
この場合は誰もデータを読み込むことも書き込むこともできません。
全員許可
{
"rules": {
".read": true,
".write": true
}
}
この場合は認証されていないユーザーでも読み込み/書き込みが可能です。
認証されたユーザーなら許可
{
"rules": {
"users": {
".read": "auth != null",
".write": "auth != null"
}
}
}
この場合は認証されたユーザーなら読み込み/書き込みが可能です。
認証本人のみ許可
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid"
".write": "$uid === auth.uid"
}
}
}
}
この場合はログインしている本人のみが本人のデータを読み込み/書き込み可能です。
認証本人のみ許可
{
"rules": {
"users": {
"$uid": {
".read": "auth != null",
".write": "$uid === auth.uid"
}
}
}
}
この場合は認証ユーザーがデータの読み取りを、ログインしている本人のみが本人のデータへの書き込みが可能です。
書き込みは本人とメンバーのみ
{
"rules": {
"users": {
"$uid": {
".read" : "auth != null",
"group": {
".read" : "auth != null",
".write": "$uid === auth.uid || root.child('users').child(auth.uid).child('shareHost').child($uid).exists()"
},
"userName":{
".read": "auth != null",
".write": "$uid === auth.uid",
},
"member":{
".read": "auth != null",
".write": "auth != null",
},
"shareHost":{
".read": "auth != null",
".write": "auth != null"
}
}
}
}
}
この場合は認証ユーザーが全てのデータの読み取りを、ログインしている本人のみがユーザーネームとグループへの書き込みが可能です。さらにメンバーがログインしている場合はシェアホストに登録されているグループデータへの書き込みも可能になっています。
root.child('users').child(auth.uid).child('shareHost').child($uid).exists()"
肝となっているこの部分はルート部分から構造を掘り下げていき、ログインユーザー/shareHost/
の中に現在読み込んでいるユーザーIDがあれば許可しています。
ルールでは存在するデータベースを順番にループしながら読みに行くので$uidには存在する全てユーザーIDが順番に格納されます。
[Firebase] Realtime Database「プロジェクトID」に安全でないルールがあります
設定しているルールが弱すぎると以下のような件名の警告メールが届きます。
[Firebase] Realtime Database「プロジェクトID」に安全でないルールがあります
このメールが届いた場合は設定しているルールを見直してあげた方が良いかもしれません。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。