A10 例外的な状況への不適切な対応

正常な道筋は試験され、例外の道筋は試験されない。攻撃者は例外を起こしてその道筋へ入り、開いたままの扉、漏れる内部の情報、半分だけ進んだ状態を使う。

OWASP Top 10:2025 A10:2025 Mishandling of Exceptional Conditions。2025年版で新しく入ったカテゴリで、対応づけられたCWEは24個[1]。2026年11月時点の記載で書いています。

要点

  • 失敗は、防げない、起きている間に気づけない、起きた後の対応がまずい、の3つの形で現れる[1]。
  • 守りの判断で例外が起きたら拒否に倒す。例外を握りつぶして処理を続ける作りは、失敗した時に開く。
  • 利用者には中身の無い誤りの応答と照合用のIDだけを返し、詳細はサーバーのログに書く。
  • 複数の手順は1つのトランザクションにし、途中で失敗したら全体を戻す。
  • 例外は起きた所で捕まえて意味のある対応をし、全体の受け止め役は最後の網として置く。

成立の条件

OWASPは原因に、入力の検証の欠落、誤りの処理が呼び出しの深い所でなく高い所に置かれていること、メモリ、権限、ネットワークの想定外の状態、例外の扱いのばらつき、捕まえていない例外を挙げる。結果は、論理の誤り、競合状態、不正な取引、認証と認可の問題として、機密性、可用性、完全性のどれをも損ない得る[1]。

形典型このページの節
防げない欠けた引数、余計な引数、0での割り算、null の参照を、入口で弾かない診断で見る所
気づけない例外を捕まえて何もしない。戻り値を確かめない失敗した時に開く
対応がまずい拒否でなく許可に倒れる。詳細を返す。途中の状態を残す。資源を放さない以下の4つの節

失敗した時に開く

権限の確認、署名の検証、外部の認可サービスへの問い合わせで例外や時間切れが起きた時、例外を握りつぶして処理を続ければ、確認が無かったことと同じになる。CWE-636 に当たる[6]。

権限の確認の結果は許可、拒否、例外の3つで、例外を許可に流すと失敗した時に開く 権限の確認 認可サービスへの問い合わせ 処理を続ける 拒否する 許可 例外 悪い例 例外 直した例 拒否
図1 確認の結果が例外だった時に、どちらへ倒すか

悪い例と直した例。架空の文書APIの権限の確認

// 悪い例 認可サービスが落ちたら、確認を飛ばして通す
let allowed = true;
try { allowed = await authz.check(user, "doc:read", docId); }
catch (e) { logger.warn(e); }  // allowed は true のまま
if (!allowed) return res.sendStatus(403);

// 直した例 既定は拒否。許可の結果を受け取れた時だけ通す
let allowed = false;
try { allowed = (await authz.check(user, "doc:read", docId)) === true; }
catch (e) { logger.error({ event: "authz_error", err: e }); return res.sendStatus(503); }
if (!allowed) return res.sendStatus(403);
  • 判断の変数の初期値を拒否にし、明示の許可でだけ通す。=== true で比べ、undefined や誤りのオブジェクトを許可と読まない。
  • 何でも受ける catch で例外を捕まえ、記録だけして続ける形は、CWE-396 と CWE-390 に当たる。レビューではまずここを探す。

詳しすぎるエラー

スタックトレース、SQLの誤り、ファイルの置き場所、部品の版を利用者に返すと、攻撃の下調べを助ける。OWASPの例では、詳しいDBの誤りを表示させて系の情報を集め、SQLインジェクションの組み立てに使う[1][2]。CWE-209 に当たる[7]。

悪い例と直した例。架空のAPIの応答

# 悪い例 誤りをそのまま返す
HTTP/1.1 500 Internal Server Error
Content-Type: text/plain

error: column "emial" does not exist
    at Parser.parseErrorMessage (/srv/shop/node_modules/pg-protocol/dist/parser.js:283)

# 直した例 中身の無い応答と照合用のIDだけを返す
HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json

{"type":"about:blank","title":"Internal Server Error","status":500,
 "errorId":"7f3c2a9e"}

RFC 9457 は、HTTP APIの誤りの応答の形式を定める。type を about:blank にした時、title はHTTPの状態の文言と同じにすべきとされ、独自の項目を足せる[3]。OWASPは、誤りの詳細はサーバーの側で記録し、利用者には一般的な応答を返し、実行環境の設定として全体の誤りの受け止め役を置くよう勧める[2]。

Express の全体の受け止め役。引数が4つの関数を、ほかの経路の後に置く[8]

app.use((err, req, res, next) => {
  const errorId = crypto.randomUUID();
  logger.error({ event: "unhandled_error", errorId, err, path: req.path });
  res.status(500).type("application/problem+json").json(
    { type: "about:blank", title: "Internal Server Error", status: 500, errorId });
});

本番の設定で、フレームワークの詳細の誤り画面とデバッグの出力を止める。CWE-215 と CWE-756 に当たる。利用者が原因の誤りは 4xx で返し、5xx は予期しない失敗にだけ使う。5xx の増加は、特定の入力で落ちている兆しとして監視する[2]。

途中で止まった複数の手順

引き落とし、入金、記録のような複数の手順の途中で失敗した時、済んだ手順を戻さなければ、状態が壊れる。OWASPの例では、攻撃者が通信を切って送金の途中で止め、全体が戻されない作りで口座を空にするか、競合状態で何度も送金する[1]。

入金で失敗した時、戻さなければ引き落としだけが残り、トランザクションなら全体が戻る 手順ごとに確定する A から引き落とし B へ入金で失敗 記録されない 引き落としだけ残る 再送すれば二重に動く 1つのトランザクション 引き落とし 入金 記録 全部確定するか 全部戻すか
図2 途中の失敗で状態を残す作りと、全体を戻す作り
// 悪い例 手順ごとに確定する。入金で例外が出ると、引き落としだけが残る
await db.query("UPDATE accounts SET balance = balance - $1 WHERE id = $2", [amt, from]);
await db.query("UPDATE accounts SET balance = balance + $1 WHERE id = $2", [amt, to]);
await db.query("INSERT INTO transfers (req_id, from_id, to_id, amount) VALUES ($1, $2, $3, $4)", [reqId, from, to, amt]);

// 直した例 1つのトランザクションにし、残高の条件と、要求ごとの一意の制約で二重の実行を拒む
await db.tx(async (t) => {
  await t.query("INSERT INTO transfers (req_id, from_id, to_id, amount) VALUES ($1, $2, $3, $4)", [reqId, from, to, amt]);
  const r = await t.query(
    "UPDATE accounts SET balance = balance - $1 WHERE id = $2 AND balance >= $1", [amt, from]);
  if (r.rowCount !== 1) throw new Conflict();
  await t.query("UPDATE accounts SET balance = balance + $1 WHERE id = $2", [amt, to]);
});  // どこで例外が出ても全体が戻る

OWASPは、複数の手順の操作が失敗したら、部分的に立て直そうとせず、トランザクション全体を戻すよう求める[1]。DBの外の副作用、たとえば外部の決済やメールの送信は戻せないので、確定の後に送るか、要求ごとのIDで何度送っても1回分になるようにする。

例外の時に放さない資源

OWASPの例では、ファイルの受け取りで例外を捕まえても資源を放さず、失敗のたびに資源が固定され、尽きるまで続く[1]。攻撃者は失敗する要求を送り続けるだけで、サービスを止められる。CWE-460 に当たる。

// 悪い例 例外が出ると close と release に届かない
const lock = await locks.acquire(userId);
const fh = await fs.open(tmpPath, "w");
await receiveUpload(req, fh);  // 途中で切断されると例外
await fh.close();
await lock.release();

// 直した例 取った順の逆に、finally で必ず放す
const lock = await locks.acquire(userId);
try {
  const fh = await fs.open(tmpPath, "w");
  try { await receiveUpload(req, fh); }
  finally { await fh.close(); }
} finally {
  await lock.release();
}

ファイルの記述子、DBの接続、ロック、一時ファイル、メモリの上限を、失敗の道筋でも放す。言語の仕組み、たとえば Java の try-with-resources や C# の using を使えば書き忘れが減る。あわせて、要求の大きさ、時間、同時の数に上限を置き、例外的な状況そのものを起こしにくくする[1]。

守り方

根本の直し方
  • 失敗を設計の対象にする。機能ごとに、起こり得る例外と、その時にどちらへ倒すかを決め、脅威モデリングと設計のレビューで確かめる[1]。
  • 守りの判断は、既定を拒否にし、例外の時も拒否に倒す。
  • 例外は起きた所で捕まえ、意味のある対応をする。全体の受け止め役は最後の網として置き、一般的な応答と照合用のIDを返す[1][2]。
  • 複数の手順の操作は1つのトランザクションにし、失敗したら全体を戻す[1]。
  • 取った資源は、失敗の道筋でも必ず放す。
  • 誤りの扱い、記録、監視、警告を1か所にまとめ、組織で同じ作り方にそろえる[1]。

次は緩和の策で、根本の直し方を済ませたうえで重ねる。

  • 回数の制限、資源の割り当て、絞り込みで、例外的な状況とサービスの妨害の危険を下げる[1]。
  • 同じ誤りが高い率で繰り返す時は、件数と期間をまとめた記録にする。記録と監視は A09 と組で設計する[1]。
  • 負荷の試験、性能の試験、侵入の試験で、例外の道筋を通す[1]。

診断で見る所

  • 例外を起こす入力 必須の引数を外す、余計な引数を足す、型を変える、0、空、巨大な値、壊れたJSON、途中で切れた本文。応答に内部の情報が出ないか、状態が残らないか。
  • 依存先の失敗 認可サービス、DB、外部APIが時間切れになった時に、許可に倒れないか。試験の環境で依存先を止めて確かめる。
  • コードの形 何でも受ける catch で記録だけして続ける所、戻り値を確かめない所、switch の default の欠落、finally の無い資源の取得。
  • 本番の設定 デバッグの出力、詳細の誤り画面、独自の誤りのページの有無。
  • 試験の手引き OWASP WSTG の誤りの扱いの試験[5]。OWASP ASVS 5.0 の V16 に、誤りの扱いの検証の要件がある[4]。

関連するCWE

A10には24個のCWEが対応づけられている[1]。このページの節に関わるものを挙げる。

CWE名前このページの節
CWE-703Improper Check or Handling of Exceptional Conditions全体の親
CWE-636Not Failing Securely失敗した時に開く
CWE-390Detection of Error Condition Without Action失敗した時に開く
CWE-396Declaration of Catch for Generic Exception失敗した時に開く
CWE-209Generation of Error Message Containing Sensitive Information詳しすぎるエラー
CWE-215Insertion of Sensitive Information Into Debugging Code詳しすぎるエラー
CWE-756Missing Custom Error Page詳しすぎるエラー
CWE-460Improper Cleanup on Thrown Exception途中で止まった手順、放さない資源
CWE-252Unchecked Return Value診断で見る所
CWE-234Failure to Handle Missing Parameter診断で見る所
CWE-478Missing Default Case in Multiple Condition Expression診断で見る所

参照

  1. A10:2025 Mishandling of Exceptional Conditions OWASP Top 10:2025
  2. Error Handling Cheat Sheet OWASP Cheat Sheet Series
  3. RFC 9457 Problem Details for HTTP APIs IETF。2023年7月、RFC 7807 を置き換え。4.2.1
  4. V16 Security Logging and Error Handling OWASP ASVS 5.0.0
  5. Testing for Improper Error Handling OWASP Web Security Testing Guide
  6. CWE-636 MITRE CWE
  7. CWE-209 MITRE CWE
  8. Error Handling Express