OWASP Top 10:2025 で自社の不具合を分類し、権限・課金・保存の点検を見直す

OWASP Top 10 で、不具合の記録から点検が手薄な場所を探す

自社のWebサービスで見つかった欠陥を、個別に直した記録として終わらせず、点検の観点から並べ直しました。対象は、2026年9〜10月に記録した9件です。Firebase・Stripeを使う小さな構成のサービスで、ファイルの閲覧権限、決済、AIの利用上限、データ保存などに関わる問題が見つかっていました。

整理に使ったのは、OWASP Top 10:2025です。2026年10月17日に取得した一次資料の各分類にある「List of Mapped CWEs」で、記録した欠陥に対応するCWEがどの分類に含まれるかを確認しました。

目的は、10分類をひと通り解説することではありません。実際に見つけて直した問題を分類し、「どの確認が足りなかったのか」「別の処理にも同じ点検を広げるべきか」を判断することです。

今回、当てはまる記録があったのは、アクセス制御、設定、設計、ログ出力、例外的な状況の扱いに関する分類でした。一方、A03・A04・A05・A07・A08には、この期間に当てはまる欠陥の記録がありません。ただし、その観点で網羅的に検査したわけではないため、欠陥が無かったとは判断していません。

「本人は使える」の先を、規則と公開ファイルで確かめる

2026年10月14日に見つかったファイルの閲覧権限の問題は、「ログインしていれば読める」というまとめの1行が規則に残っていたことでした。規則は和集合として働くため、他人の録音を読める状態になっていました。これは、実際に誰かの録音が読まれたと確認した記録ではなく、規則上それが可能だったという記録です。

修正では、名前で挙げた場所だけに規則を書く形に変更しました。そのうえで、許可されない操作を確かめる負のテストを22件用意しました。古い規則に戻すと、そのうち3件が落ちることも確認しています。修正後にテストが通るだけでなく、以前の問題をテストが捉えられるかまで確かめました。

同日には、使っていないサーバ用のAPIキーが公開JSに含まれて配られていた問題も見つかりました。環境変数から外して出し直し、全チャンクを検査して該当が0件になったことを確認しました。翌日にはキーを作り直して旧キーを削除し、旧キーが400で拒否されることも実測しています。

さらに10月15日には、ビルドの既定で公開されていたソースマップ125本のうち1本に、コメントで残した未使用の別のFirebaseの設定値が含まれていました。該当部分をコードから削除し、次の設定で出し直しました。

GENERATE_SOURCEMAP=false

本人が画面を操作できるかという確認と、他人に何が見えるか、公開ファイルに何が含まれるかという確認は、点検する対象が異なります。今回のアクセス制御に関する2件では、「本人は使える」ことしか試していなかった点が共通していました。

課金とAIの上限は、サーバ側の判断を点検する

9月29日に記録した決済の問題では、決済画面を作る関数が、ブラウザから送られた価格IDを確かめずに決済サービスへ渡していました。決済のアカウントを複数の製品で共用していたため、検証用の安い価格で有料になれる状態でした。この件の実害は0と記録しています。

修正では、売ってよい価格の一覧をサーバ側に持ち、それ以外は400で拒否するようにしました。決済完了の通知を受ける際にも、自分の製品の購入かを確認する形にしています。

ここで見直したのは、コードの1行だけではありません。「ブラウザから渡された価格を、そのまま信じる」という設計です。同じ判断の仕方が残っていないか、他の課金経路にも点検を広げました。

10月12日には、AIを呼ぶ機能に連打の制限が無いことも記録しました。修正後は60秒に3回までとし、断るときには待つ秒数を返します。作ったばかりの無料口座については、1日3回としました。

ただし、上限を設けるだけでは確認は終わりません。上限の数を数える処理が失敗したときに、その制限がどう振る舞うかも別に点検する必要がありました。

正常に見える画面でも、例外時の処理は止まっていた

9件のうち最も多かったのは、A10「例外的な状況の扱い」に当たる3件でした。記録した欠陥と修正内容は、次のとおりです。

見つけた欠陥(日付) 当たる分類 CWE(OWASP の対応表にあるもの) 直し方
上限の数を数える処理が失敗したとき、catch で上限なしで通していた(コメントは「quota is a guard, not a wall」)。データベースが不調な日は上限が同時に外れる(2026-10) A10 CWE-636 Not Failing Securely ('Failing Open') 数えられないときは実行しない(fail closed)に反転
保存がデータベースの規則で拒否され続けていたのに、画面は正常に見えていた(拒否を握りつぶしていた)。別の製品で、最後に保存できた日から約10か月、記録が残っていなかった(2026-09-26) A10 CWE-390 / CWE-391(エラーを検知して何もしない・確かめない) 書いた後に読み戻して存在を確かめる段を、毎日の通し試験に入れた
通信の await に時間切れが無く、回線が切れると catch に入らず無限に待つ。購読状態の確認が止まり、ログイン済みなのにログイン前の見た目のまま(2026-09-26) A10 CWE-754 / CWE-755 時間切れを付け、時間切れは「確認できなかった」という独立の状態にする(「未課金」「0件」に丸めない)

この3件は、エラーが出なかったり、画面が正常に見えたりするため、見た目の確認では見つからない問題でした。発見につながったのは、通信をわざと切る試験、保存後の読み戻し、本番データの件数の確認です。

保存については、画面の操作が完了したことをもって成功とせず、書いたデータが存在することを読み戻して確認する段を、毎日の通し試験に加えました。通信については、時間切れを設けるとともに、「確認できなかった」を独立した状態として扱いました。確認できていない購読状態を「未課金」にしたり、取得できていない件数を「0件」にしたりしないためです。

上限の確認では、数えられないときに実行を許していた処理を、実行しない処理へ変更しました。正常時の上限値と、確認処理が失敗したときの判断を、両方点検したことになります。

検査する道具の出力も確認する

10月には、検査ツールの出力に、見つけた秘密の値そのものが出る作りにも気づきました。これはA09に当たり、対応するCWEは「CWE-532 Insertion of Sensitive Information into Log File」です。

対処として、出力は伏せてから見るようにしました。秘密を含むファイルについては、ファイル名・件数・長さだけを見る決まりにしています。

公開JSやソースマップを検査する際には、何が含まれているかを確かめる必要があります。その一方で、検査結果に値そのものを出してしまえば、出力側にも秘密が残ります。今回の点検では、サービスのコードや公開ファイルに加え、それを調べる道具が何を出力するかまで確認対象にしました。

業務に置き換えると

社内システムや小さなWebサービスを点検する際には、今回の記録を次の確認項目に置き換えられます。

  • 不具合の記録を分類する。 自分たちが見つけた問題をTop 10に並べ直し、どこが手薄かを確認します。当てはまる記録が無い分類は、検査済みなのか、まだ確かめていないのかを区別します。
  • 許可しない操作も試す。 「本人はできる」に加え、「他人はできない」「ログインしていない人はできない」を試験に含めます。
  • 確認に失敗したときの分岐を読む。 上限・認証・課金の確認が失敗した際、そのまま通す処理になっていないかをコードで探します。
  • 公開ファイルを実際に取得する。 JSやソースマップに、秘密の値や残したコメントが含まれていないかを確認します。
  • ログと検査ツールの出力を見る。 秘密の値そのものが出ないかを点検し、確認に使う情報をファイル名・件数・長さなどに絞ります。

この記事は、TechAthletes が自社のサービスを作る中で記録した実測をもとに書いています。業務への導入のご相談は 仕事の窓口 で受け付けています。

✨ Claudeエージェントを複数走らせるならAgent Desk
Terminal tile manager for macOS。全ソース付き $10 買切り。
Agent Desk を見る →

投稿者 TechAthletes 編集部

TechAthletes は、AI・クラウド・開発ツールの技術記事を公開しているメディアです。記事内の商品リンクには広告(アフィリエイトリンク)が含まれます。

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

Click to listen highlighted text!