APIセキュリティ入門:REST・認証・認可
マルチテナントAPIの分離
この回でやること
複数の出店者が入るショップで、出店者Aのトークンで出店者Bの注文を読めてしまう形です。クエリに必ずテナントの条件を入れること、入れ忘れを機械で見張ることを扱います。
- 読む 約 8 分
- 最後にクイズ 1 問
このレッスンで学ぶこと
マルチテナントは、練習用のショップのように、複数の出店者(テナント)が一つのAPIに同居する作りです。この回は、出店者Aのトークンで出店者Bの注文を読めてしまう形と、その直し方を扱います。
読み終えるころには、一覧を返すクエリを見て、どの一行が店の境界を担っているかを指せるようになります。
APIでは、境界がクエリの条件だけになる
画面のあるアプリでのテナント分離は、認証・認可編で一度見ました。APIで変わるのは、テナントの境界が画面の切り替えではなく、サーバーが投げる1本1本のクエリの条件だけで守られている点です。境界は目に見えるUIではなく、WHEREの一行になります。
形は2つあります。1つはGET /api/orders?status=paidのような一覧APIが、ログイン中の出店者に関係なく全店の注文を返すもの。もう1つはURLに他店の識別子を入れて叩けるものです。
http
GET /api/sellers/aoba/orders
Authorization: Bearer <midori のトークン>引く母集合を、自分の店に絞る
根本は、サーバーのクエリが絞り込みを1本落としていることです。
SQL クエリ
SELECT id, buyer, total_yen FROM orders
WHERE status = 'paid'
AND seller_id = ? -- この一行が抜けているオブジェクト単位の認可は引いた1件の持ち主を見る話でしたが、テナントは引く母集合そのものを自分の店に絞る話です。1件ずつ確かめる前に、そもそも他店の行を取り出さない、という段が先に立ちます。
?seller_id=aobaやURLのaobaをそのまま条件に使うと、詐称し放題です。絞り込みに使う出店者は、必ずトークンから引いた値にします。リクエストが名乗るテナントは信じません。
入れ忘れを機械で見張る
エンドポイントが増えるほど、どこか1本でseller_idの条件を書き落とします。人手のレビューだけでは必ず抜けます。テナントを持つテーブルへのクエリは共通の関数を必ず通し、出店者の条件が付いていないクエリを静的に弾く、という見張りをコード側に置きます。
手を動かして確かめる
練習用の環境で、midoriのトークンで一覧を取り、返る各注文のseller欄がすべてmidoriかを見ます。aobaの注文が混じっていれば分離が抜けています。URLに他店の識別子を入れられる作りなら、/api/sellers/aoba/ordersをmidoriのトークンで叩き、200で返らないかを確かめます。確かめるのは自分が権限を持つ練習用の環境だけにします。