コース目次 / 第2章
ルールを締める — 認証と所有者を要求する
認証情報と所有者一致を要求するルールに直し、同じ攻撃が拒否されることを再検証します。本物のFirestoreルール構文との対応と、既定拒否の原則を確認します。
前章のモックを、OPEN=1 を外して起動するだけで、実は直っています——コード自体には、すでに認可チェックが書いてありました。それを、あらためて読み解きます。
直っているコード
if (!OPEN) {
const uid = auth ? auth.replace('Bearer ', '') : null;
if (uid !== doc.owner) { res.writeHead(403); res.end(JSON.stringify({ error: 'PERMISSION_DENIED' })); return; }
}
1. 認証情報(Authorization ヘッダー)が無ければ、uid は null——doc.owner(“u1” や “u2”)と一致するはずがないので、拒否されます。
2. 認証情報があっても、それが「そのドキュメントの持ち主」でなければ、やはり拒否されます。
これは、Firestore の実際のルール構文で書けば、こういう形に相当します。
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}request.auth != null(ログインしているか)と、request.auth.uid == userId(本人か)。モックのコードでやっていたことと、まったく同じ2段の確認です。
再検証
node mock-firestore.js # OPEN を付けない = 締まったルール
# 認証なし
curl -s "http://127.0.0.1:4300/v1/projects/demo-app/databases(default)/documents/users/u2"
# => {"error":"PERMISSION_DENIED"}
# 他人(太郎)として、花子のドキュメントを読む
curl -s -H "Authorization: Bearer u1" \
"http://127.0.0.1:4300/v1/projects/demo-app/databases(default)/documents/users/u2"
# => {"error":"PERMISSION_DENIED"}
# 本人(花子)として
curl -s -H "Authorization: Bearer u2" \
"http://127.0.0.1:4300/v1/projects/demo-app/databases(default)/documents/users/u2"
# => {"owner":"u2","name":"架空花子", ...}(正しく読める)
認証なし・他人としてのアクセスは拒否され、本人としてのアクセスだけが通ります。sec-authz の「水平の権限昇格」への直しと、寸分違わぬ形です——資源を返す直前に、認証済みか・本人かを確認する。BaaS の世界でも、認可の原則は変わりません。
既定は拒否 — ルールにも、同じ発想
実際の Firestore・Firebase Storage は、ルールを何も書かなければ、既定ですべて拒否する設計です(allow read, write: if false; 相当)。sec-securecoding の「既定は拒否、例外は理由付きで最小に」が、ルールエンジンの設計そのものに現れています。
実際のプロジェクトを持っているなら、Firebase CLI のエミュレータ(firebase emulators:start)を使うと、本物のルールに対して、このコースでやったのと同じような確認を、安全な模擬環境で行えます。
持ち帰る一言
ルールは、認証と所有者の2段で締める。 「ログインしているか」と「本人か」——この2つを、資源へのアクセスを許す条件式として明示する。サーバコードが無くても、認可の原則(事実は正しい場所で確認する)は変わりません。次はまとめて、このコースを締めます。
こうなっていればOK
卒業まであと1章です。
この章はまだ完了していません。
保存できませんでした(プライベートブラウズ中かもしれません)。この端末に進捗は残りませんが、先へは進めます。