こう 聞かれます
- 「他人の
データを 見られる 穴を 防ぐ 権限の 仕組みを、 どう 作りますか」 - 「URL の
/orders/123の数字を 124 に 変えたら、 他人の 注文が 見えてしまいました。 どう 直しますか」 - 「画面に
他人の ボタンを 出さなければ、 十分ではないですか」 (裏から聞く形)
面接官が 見ている点
- ログインしているか (認証) と、
その物を 触って よいか (認可) を 分けて 考えられるか - 画面から
来た ID や 役割を 信じず、 サーバーで 毎回確かめるか - 確かめ忘れが
起きにくい作り (問い 合わせに 条件を 入れる、 判断を 1 か所に 集める ) に しているか - 別の
利用者で 試して、 穴が 無い ことを 確かめるか
短い答え
- 画面や URL から
来た ID は 信じず、 サーバーで 毎回 「いまの 利用者が、 この 物を 触って よいか」を 確かめます。 ログインしているかだけでは 足りません。 - 確かめ忘れが
起きない よう、 データを 取る 問い 合わせ その ものに 「持ち主がいまの利用者」の 条件を 入れ、 誰が 何を して よいかの 判断は 1 か所に まとめます。 許すと 決めた 物以外は 断ります。 - 別の
利用者の ID や鍵で 同じ API を 叩く 試験を 用意し、 他人の 物が 返らない ことを 確かめ続けます。
この
少し 詳しい答え
なぜ穴ができるか
ログインの
画面に
問い 合わせに 持ち主の 条件を入れる
取ってから
JavaScript
const orders = [
{ id: 1, ownerId: "alice", item: "本" },
{ id: 2, ownerId: "bob", item: "靴" },
];
function getOrderUnsafe(orderId) {
return orders.find((o) => o.id === orderId) ?? null;
}
function getOrder(currentUser, orderId) {
const order = orders.find(
(o) => o.id === orderId && o.ownerId === currentUser.id
);
if (!order) {
return { status: 404 };
}
return { status: 200, item: order.item };
}
const alice = { id: "alice" };
console.log(getOrderUnsafe(2).item);
console.log(getOrder(alice, 1));
console.log(getOrder(alice, 2));
console.log(getOrder(alice, 999));靴
{ status: 200, item: '本' }
{ status: 404 }
{ status: 404 }getOrderUnsafe は、getOrder は、currentUser は
他人の
SQL で
SQL クエリ
SELECT * FROM orders WHERE id = $1 AND owner_id = $2;判断を 1 か所にまとめる
役割 (管理者、
判断の
別の 利用者で試す
利用者 A で
話すときは、
よく ある間違い
- ログインしているかだけを確かめる
ログインした 人なら誰でも、 ID を 変えて 他人の データを 見られます - 画面から
送られた 利用者の ID や 役割を信じる 送る 値は 書き換えられます。 利用者は サーバーが ログインの 情報から決めます - 画面に
ボタンを 出さない ことで 守った つもりになる API は 直接叩けます。 守りは サーバーに置きます - 権限の
確かめを API ごとに ばらばらに書く 1 か所の 書き忘れが、 そのまま 穴になります
深掘りの問い
- 「ID を
推測しにくい乱数の 文字列に すれば 安全ではないですか」 当てにくくは なるが、 ID が 漏れれば 見えるので、 持ち主の 確かめは 別に 要る、と答えます - 「一覧の API では、
どう 守りますか」 一覧の 問い 合わせにも 持ち主や チームの 条件を 必ず 入れ、 検索の 条件は 画面から 来た値で 広げられないように する、と答えます - 「管理者の画面は」管理者の
操作は 別の 入口と 強い 確かめにし、 誰が 何を 見たかを 記録に 残す、と答えます
