このページはコミュニティーの尽力で英語から翻訳されました。MDN Web Docs コミュニティーについてもっと知り、仲間になるにはこちらから。

View in English Always switch to English

安全でない直接オブジェクト参照 (IDOR)

安全でない直接オブジェクト参照 (IDOR) とは、攻撃者が、不十分なアクセス制御や、データベースのキーやファイルパスなどのオブジェクト識別子の不適切な公開を悪用することができる脆弱性です。

ウェブサイトでは、ユーザーごとに異なるコンテンツを提供したい多くの場合があります。例えば、ショッピングサイトでは、それぞれのユーザーが自分の購入履歴を確認できるようにすることがあります。ウェブサイトは、パスワードやパスキーなどのメソッドを用いてユーザーを認証することで、ユーザーを識別することができます。多くの場合、ウェブサイトがユーザーを認証すると、そのユーザーのブラウザーにセッションクッキーが設定されます。そうすることで、ユーザーがリクエストを送信した際、サーバーはそのリクエストが認証済みのユーザーからのものであることを認識できるようになります。

ただし、リクエストが認証済みのユーザーから送信されたものであることを確認するだけでなく、サーバーはユーザーがリクエストしたリソースに対するアクセス制御を実施する必要があります。つまり、そのユーザーがリクエストした特定のリソースへのアクセスが許可されているかどうかを確認しなければなりません。例えば、それぞれの認証済みユーザーには、自身の購入履歴のみを閲覧できるようにする必要があります。

サーバーがリソースに対するアクセス制御を実装していない場合、サイトにログインしている攻撃者が、別のユーザーに属するリソースにアクセスできてしまう可能性があります。これは、安全でない直接オブジェクト参照 (IDOR) 攻撃と呼ばれます。

シナリオ例

典型的な IDOR 攻撃は、サーバーがユーザーの認証のみを調べ、そのユーザーがオブジェクト参照へのアクセス権限を持っているかどうかを確認しない場合に現れます。一般的な攻撃のフローでは、攻撃者は次のように行動します。

  1. 通常のユーザーとしてログインします。
  2. ユーザーやリソースの ID(例: 1234)を参照している URL、フォームフィールド、ファイルを探します。
  3. ID を別の値(例: 1233)に変更します。
  4. 他のユーザーのデータに認証されずにアクセスできます。

以下の節で、この攻撃の具体的な例をいくつか見ていきます。

URL の改ざん

一般的な IDOR 攻撃の手口の一つに、URL 内の直接オブジェクト参照の改ざんがあります。以下の URL にある "1234" は、サーバーのデータベース内にあるユーザーレコードの識別子です。攻撃者がこの数値を別の数値(例: "1235")に変更し、他のユーザーの情報にアクセスできてしまう場合、そのアプリケーションは「安全でない直接オブジェクト参照 (IDOR)」の脆弱性があることになります。

http
# 攻撃者がユーザー 1234 としてログインする
https://example.org/user/id/1234

# 攻撃者が URL 内の ID を変更し、別のユーザーのアカウントにアクセスする
https://example.org/user/id/1235

例えば、下記の Express のコードでは、URL で指定された値は req.params.id として利用可能であり、その値を使ってデータベースから対応するレコードを取得しています。同時に、(isAuthenticated 関数を呼び出すことで)リクエストが認証済みユーザーからのものであるかどうかも調べています。しかし重要な点として、認証済みユーザーの ID が URL 内の ID と一致するかどうかは調べておらず、このため、ある認証済みユーザー(攻撃者)が、別の認証済みユーザー(被害者)のページを取得することができるのです。

js
app.get("/user/id/:id", (req, res) => {
  const user = db.users.find(req.params.id);
  if (req.isAuthenticated()) {
    // 認証だけでは不十分!
    res.render("user", { user });
  }
});

その代わりに、ユーザー情報へのアクセスを許可するためのルールを実装する必要があります。例えば、ログイン中のユーザー ID がリクエストされたユーザー ID と一致する場合にのみ、ユーザーページを表示するようにします。一致しない場合は、HTTP の 401 Unauthorized レスポンスを返します。

js
app.get("/user/id/:id", (req, res) => {
  const user = db.users.find(req.params.id);
  if (req.isAuthenticated() && req.session.userId === req.params.id) {
    res.render("user", { user });
  } else {
    return res.status(401).json({ message: "Unauthorized" });
  }
});

文書の操作

URL の改ざんと同様に、攻撃者はブラウザーの開発者ツール内で、ラジオボタンやチェックボックス、あるいは(非表示の)<input> 要素といった <form> 要素の値を変更することで、ページの本文を改ざんすることが可能です。 例えば、アプリケーションが URL にユーザー ID を提供せず、代わりに非表示のフォーム要素を通じてユーザー ID を渡している場合を考えてみましょう。

html
<form action="updateUser" method="POST">
  <input type="hidden" name="user_id" value="1234" />
  <button type="submit">プロフィールを更新</button>
</form>

サーバー側のアクセス制御が行われていない場合、攻撃者は非表示の <input> 要素内の user_id の値を別のユーザー ID に改ざんし、権限なくプロフィールを変更することが可能です。

ファイルアクセス

IDOR 攻撃の特殊な場合として、アクセス制御によって保護されていないファイルやディレクトリーへのアクセスが挙げられます。例えば、PDF ファイルのアップロード用にフォルダーを用意し、アップロードされたファイルに名前が付いている場合、アクセス制御が施されていないと、攻撃者はファイル名を推測してそれらをすべてダウンロードできてしまいます。また、保護されていないディレクトリー内にあるサーバーの設定ファイルなどの他のファイルも入手される可能性があり、さらなる脆弱性につながる恐れがあります。

http
https://example.org/static/pdfs/1.pdf
https://example.org/static/pdfs/2.pdf

IDOR に対する防御策

それぞれのオブジェクトに対するアクセス制御

IDOR 攻撃に対する最も重要な対策は、ユーザーがアクセスを試みるそれぞれのオブジェクトに対して、サーバーサイドでのアクセス制御チェックを実装することです。認証済みのユーザーが、対象のオブジェクトにアクセスしたり、そのオブジェクトに対して操作を実行したりする権限を持っていることを、常に確認してください。

識別子の複雑化

リソースの識別子を攻撃者に見破られないよう対策してください。URL には、ユーザー名やメールアドレスなどの個人を特定できる情報 (PII) を一切公開しないでください。代わりに、ユーザーを表すために、固有で推測不可能なトークンを使用してください。 UUID のような、より複雑な ID を主キーとして使用することができます。これにより、有効な値を推測しにくくすることができます。ただし、これは有効な ID が推測される可能性を低減するに過ぎず、適切なアクセス制御が必要であるという事実を置き換えるものではありません。

防衛の要約チェックリスト

  • 認証されたユーザーが、そのオブジェクトにアクセスしたり変更したりする権限を持っていることを、常に確認してください。
  • 予測可能、連番、機密性の高いオブジェクト識別子(ユーザー ID やメールアドレスなど)を公開することを避けましょう。
  • 予測されにくい、より複雑な ID(例えば、UUID など)を使用してください。

関連情報