認証・認可の脆弱性:ログインとIDOR
テナント分離 他社のデータを見せない
この回でやること
複数の出店者が入るショップで、出店者Aが出店者Bの注文を見てしまう形を扱います。データを引くクエリに必ずテナントの条件を入れ、入れ忘れを機械で見張る直し方を見ます。
- 読む 約 8 分
- 最後にクイズ 1 問
このレッスンで学ぶこと
テナント分離は、同じ仕組みに相乗りする出店者どうしの境界を守り、他社のデータを見せないことです。この回は持ち主の確認の単位を利用者ひとりから組織に上げ、その境界が抜ける形と直し方を扱います。
読み終えると、データを引く口のどこにテナントの条件が要るかを、自分のクエリで数えられるようになります。
出店者Aから、出店者Bの注文が見える
出店者Aとしてログインし、自分の店の注文を開きます。次に、番号を出店者Bの店の注文2087に変えて開きます。
プレーンテキスト
GET /api/orders/1043 出店者A 自分の店の注文
GET /api/orders/2087 出店者B の店の注文が返る利用者ひとりのIDORと形は似ていますが、またいでいる境界が違います。ここで漏れているのは、ある個人の持ち物ではなく、別の会社のデータがまるごと見えるという組織の境界です。1件が見えるということは、たいてい一覧や検索でも他店の行が混じることを意味し、被害は個人のIDORより広くなります。
抜けるのは、クエリのどこか1か所
なぜ他店の行が出るのか。注文を引くクエリに、操作中の出店者を絞る条件が入っていないからです。
プレーンテキスト
SELECT * FROM orders WHERE id = 2087
→ 出店者の条件が無いので、どの店の注文でも引ける正しくは、店を絞る条件を必ず併せます。
プレーンテキスト
SELECT * FROM orders
WHERE id = 2087 AND shop_id = :操作中の出店者の店
→ 他店の行は結果に出てこない厄介なのは、この条件がクエリごとに要ることです。注文の照会、一覧、検索、集計、明細と、データを引く口はいくつもあります。人手で全部に条件を入れて回ると、必ずどこかで抜けます。BOLAの回で見たとおり、口の数を人が数え切るのは難しいのです。
直し方は、条件を必ず通し、抜けを機械で見張る
要点は、テナントの条件を「気をつけて毎回書く」に頼らないことです。
- データを引く共通の入口を1か所に置き、そこが操作中の出店者の条件を必ず足す。個々のクエリが書き忘れても、入口で必ず絞られる
- データベース側で行ごとの見える範囲を出店者に縛る仕組みを使う。これに対応するかは製品によります
- テナントの条件が無いクエリを、テストや静的な検査で機械的に見つけて落とす
テナント分離は、1つの口が抜けただけで会社のデータが混ざります。人の注意力に賭けず、条件を必ず通す仕組みと、抜けを見張る機械の両方を持ってください。