web-pentest について
web-pentest は、許可を得た Web アプリを対象にブラックボックスまたはグレーボックスで検査するスキルです。OWASP WSTG に沿って段階的に確認し、発見事項には基準となる応答と攻撃時の証拠を求めます。
主な機能
- 01OWASP WSTG に沿った段階的テスト
- 02認証とアクセス制御の検査
- 03入力検証とビジネスロジックのテスト
- 04ソースコードと稼働環境の照合
- 05発見事項の対になる証拠の記録
活用シーン
- 01許可を得た稼働中 Web アプリの評価
- 02IDOR、権限、セッションの問題の検査
- 03ソースレビューの手掛かりを実環境で検証
- 04再現可能な発見証拠の収集
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ briiirussell/ cybersecurity-skills --skill web-pentestインストール後、Claude Code で/web-pentestを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/findings-format.mdfindings.schema.jsonseverity-calibration.mdto-sarif.mjsvalidate-findings.mjsverification.md詳細
Web Pentest — ライブ Web アプリケーションのテスト
許可されたターゲットに対する、稼働中の Web アプリケーションの体系的なブラックボックス/グレーボックス侵入テストです。サーフェスをマッピングする recon と組み合わせて使い、ソースコードを調査する owasp-audit を補完します。まず recon を使い、ターゲットのリストと認証情報(またはゲストアクセス)を用意してから、このツールを使ってください。
許可の確認
ターゲットにアクセスする前に、次の項目を確認してください。
- 対象のアプリケーションに対する書面での許可があること(侵入テストの契約、バグバウンティの対象ドメイン、CTF/ラボ、自身が所有する資産)
- アプリケーションが現在も対象範囲内で、稼働中であること(廃止済みではなく、メンテナンスによる停止中でもない)
- テスト用認証情報が提供されていること(グレーボックスの場合)、またはゲストアクセスが確認できていること(ブラックボックスの場合)
- 対象範囲外の項目が明記されていること — 本番環境のユーザーデータ、決済フロー、ソーシャルエンジニアリング、DoS
不明な点があれば、作業を始める前に確認してください。許可があると決めつけてはいけません。
方法論
OWASP Web Security Testing Guide(WSTG)の構成に従います。各フェーズで証拠を収集し、作業を進めながらすべてを記録してください。
フェーズ 1:設定とデプロイ
目標:稼働中の構成と公開方法を把握します。
- HTTP ヘッダー —
curl -Iとcurl -I -H "Origin: https://evil.com"で取得します。- スタックを明らかにする
Server、X-Powered-By - HSTS、CSP、X-Frame-Options、X-Content-Type-Options の有無と値
- CORS — 許可リストがない場合の
Access-Control-Allow-Originの反射、*と組み合わされたAllow-Credentials: true
- スタックを明らかにする
- TLS —
testssl.sh https://targetまたはsslyze --regular target - 公開されているバックアップ/設定ファイル —
/.git/、/.env、/backup.sql、/web.config、/server-status、/.aws/credentials - デフォルトの管理者用パス —
/admin、/administrator、/manage、/console、/actuator/、/_debug/、/swagger、/graphql、/.well-known/ - Robots.txt と sitemap.xml — インデックスには登録されていないものの、アクセス可能なエンドポイントが記載されていることがよくあります。
- サブドメインと Host ヘッダーのテスト —
Host: internal.target.comを送信すると、アプリケーションの挙動が変わりますか?
フェーズ 2:ID 管理
目標:どのようなユーザーを、どのように作成でき、どのような権限を付与できるかを把握します。
- 登録 — 自分で登録できますか?ロールのデフォルト値が「user」以外になっていませんか?
- 登録時(「このメールアドレスはすでに登録されています」)、ログイン時(「ユーザーが見つかりません」と「パスワードが違います」)、パスワードリセット時(「メールを送信しました」と「該当ユーザーがいません」)の応答の違いによるユーザー名の列挙
- アカウントのプロビジョニング — 確認メールが届きますか?省略できますか?リセットリンクは期限切れになりますか?リセットリンクは元のセッションにひも付いていますか?
- アカウントロックアウトとレート制限 — ログインに100回失敗するとアカウントがロックされますか(サービス拒否の攻撃経路)、それとも処理が遅くなるだけですか(望ましい動作)?
フェーズ 3:認証
目標:アクセスすべきでないユーザーとして侵入します。
- ログインフォーム — HTTPS が強制されていますか?認証情報が URL パラメーターで送信されていませんか(あらゆる場所に記録されます)?
- パスワードポリシー — 最小文字数/複雑さ/漏えいデータベースの照合(haveibeenpwned API)
- MFA — 管理者/特権ユーザーに必須ですか?バイパス経路(リカバリーコード、「このデバイスを記憶する」Cookie の永続化、SMS へのダウングレード)はありますか?
- セッショントークン — 十分なエントロピーがありますか?Cookie に
HttpOnly、Secure、SameSite=LaxまたはStrictが設定されていますか? - セッション固定 — ログイン時にセッション ID がローテーションしますか?認証前のセッション ID は認証後も有効ですか?
- 「ログイン状態を保持」 — 別の Cookie に有効期間の長いトークンがありますか?すべてのデバイスからログアウトしたときに失効させられますか?
- ログアウト — サーバー側でセッションが実際に無効化されますか?(ログアウト後に Cookie を再送してテストします。)
- パスワードリセット — トークンの形式、有効期限、単回使用、Host ヘッダー汚染(リセットメールのリンクに
Host: evil.comを指定) - OAuth フロー — PKCE が使われていますか?
stateパラメーターが検証されていますか?redirect_uriは厳密に照合されていますか(前方一致ではなく)?
フェーズ 4:認可(最も成果が得られるフェーズ)
目標:本来アクセスできないものにアクセスします。
水平権限昇格(IDOR / BOLA):
- ユーザー A としてサインインし、ユーザー A が所有するリソースを取得します。ID を控えます。
- ユーザー B としてサインインし、ID を指定してユーザー A のリソースにアクセスします。直接取得する、更新リクエストの本文に含める、別の操作で外部キーとして使う、といった方法を試します。
- レスポンスコードを確認します。200(問題あり)、403(適切)、404(不正なリクエストがすべて 404 を返すなら適切。自分自身の 404 と異なる場合は問題あり)
- ID を受け取るすべてのエンドポイントを試します。
GET /users/:id、POST /users/:id/...、DELETE /users/:id、GET /api/users/:id/orders、GraphQL のuser(id: ...)
垂直権限昇格(BFLA):
- 管理者専用のルートを特定します(
/admin/*の調査、JS バンドル内の文字列、Burp のサイトマップなどから) - 一般ユーザーとしてアクセスします。403(適切)、200(非常に問題あり)
- 動詞の改ざんを確認します。
DELETE /admin/users/xはブロックされるが、POST /admin/users/x/deleteは機能する、といったケースです。 - マスアサインメント:
User.create(req.body)がフィルタリングなしで実行されるため、{ role: "admin" }を受け付ける登録エンドポイント
テナント分離(マルチテナント SaaS):
- ユーザー U1 で組織 A を作成します。ユーザー U2 で組織 B を作成します。
- U1 として、組織/テナント ID を含む URL をすべて試し、組織 B の ID に置き換えます。すべて 403/404 になることが期待されます。200 が返る場合はテナント分離の不備です。
フェーズ 5:セッション管理
- セッショントークンが URL に含まれていませんか?ログに記録される場所ならどこでも、アクセスログから漏えいします。
- 複数の同時セッションが許可されていますか?設定可能で、ログに記録できる必要があります。
- パスワード変更後、トークンはすべての場所で無効化されますか?(ブラウザー 1 でパスワードを変更し、ブラウザー 2 の Cookie を再送信してテストします。)
- CSRF 対策はありますか?同期トークン、SameSite Cookie、カスタムリクエストヘッダーなどです。異なるオリジンからリクエストを偽造してテストします。
フェーズ 6:入力検証
各入力(URL パラメーター、クエリー文字列、ヘッダー、JSON 本文、ファイルアップロード)について確認します。
- XSS — 反射型(エラーページ、検索結果、
<title>への挿入)、蓄積型(プロフィール項目、コメント、ファイル名)、DOM 型(innerHTML、eval、locationなどの JS シンクを調べます)- ペイロード集 —
owasp-auditの Verify Fixes XSS ペイロードを参照してください
- ペイロード集 —
- SQLi —
' OR 1=1 --、時間ベースのブラインド SQLi('; SELECT pg_sleep(5)--)、エラーベース、UNION ベース- 最近の技術スタックでは通常、パラメーター化クエリーが使われています。対応が漏れているエンドポイントを探します。
- コマンドインジェクション — ファイル名、画像処理、PDF 生成、シェルコマンドの実行が行われる箇所
- SSRF — Webhook URL、画像の取得、PDF のレンダリング。
owasp-auditの A01 SSRF バイパスマトリックスを参照してください - XXE — SOAP/SVG アップロード/DOCX インポートの各パスにおける XML 入力
- サーバーサイドテンプレートインジェクション — 名前フィールドに
{{7*7}}を入力すると49と表示されますか?その場合は侵入成功です。 - オープンリダイレクト —
owasp-auditの A01(制御文字の拒否)を参照してください - 安全でないデシリアライズ — 不透明なトークンを Base64 デコードし、シリアライズ形式のマーカー(PHP の
O:、Java の\xac\xed、Python pickle のcos\nsystem)を探します。
フェーズ 7:エラー処理
- 500 レスポンスにスタックトレースが表示される
- データベースエラーからスキーマ(テーブル名、カラム名)が漏えいする
- ファイルパスからサーバーのファイルシステム構成が漏えいする
- ユーザー名が有効か無効かでエラーが異なる(列挙が可能)
/health//status//metrics//actuator/*が公開されている
フェーズ 8:ビジネスロジック
ほとんどのスキャナーが手を出せないカテゴリです。アプリが本来どのように動作するべきかを確認し、「その操作を誤った方法で行ったらどうなるか?」と考えます。
- 競合状態 — 同じクーポンコードを同時に100回送信した場合、複数回利用できますか?
- 負の数 —
-100を送金して、自分の残高に加算できますか? - 順序が異なるワークフロー — ステップ2より先にステップ4を完了できますか?
- クォータの回避 — 無料プランの制限はサーバー側で適用されていますか?それともUIだけですか?
- ワークフローの承認 —
approver_idを改ざんして、自分のリクエストを承認できますか? - ファイルのアップロード —
.htmlをアップロードし、サーバーにContent-Type: text/htmlで配信させることができますか?
不変条件のモデリング、Actor x Action x Resourceマトリクス、ステートマシンの悪用、上限値の小分け、数値/返金裁定のバグ、二重支払いを証明する並行処理の手法(HTTP/2単一パケット攻撃、冪等性キーのスコープ、読み取り・変更・書き込みの更新消失)について詳しく知りたい場合は、business-logic-testingを使用してください。資金の流れに関する並行処理の証明には、明示的な書面でのスコープとリクエスト上限が必要です。ステージング環境/テストモードを推奨します。
フェーズ 9:クライアント側
- JSバンドルの確認 —
view-source:/DevToolsで、インラインのシークレット、APIキー、内部エンドポイントのヒントを探します - CSP —
<script>の注入を試し、ブロックされるか確認します - ブラウザーのストレージ —
localStorage/sessionStorage/IndexedDBには何が保存されていますか?トークンですか?個人を特定できる情報ですか? - クロスオリジン — オリジンチェックなしで、自分のオリジンからの
postMessageをアプリのウィンドウに届かせることができますか?
ホワイトボックス+ブラックボックスの併用モード
同じターゲットについて、ソースコードと稼働中のインスタンスの両方がある場合に実行します。owasp-auditはコードを読み、このスキルは稼働中のアプリをテストします。組み合わせることで、どちらか一方だけの場合より効果が高まります。コードからバグの場所がわかり、稼働中のアプリのテストで、そのバグに到達して悪用できることを証明できます。(owasp-auditはソースコードと稼働中のアプリを併用するケースとして、ここにリンクしています。)上記のAuthorization Checkは、稼働中のターゲットにも引き続き全面的に適用されます。ソースコードを読んでも、リクエストの送信が許可されるわけではありません。
Target Mapを作成します。 各コードハンドラーと、それが処理する稼働中のエンドポイントを結び付けるテーブルを作成します。共通の認証情報と設定コンテキストを使い、コードを見て作成したリクエストが稼働中のインスタンスに届くようにします。
| コードハンドラー | 稼働中のエンドポイント | 認証コンテキスト | メモ/手がかり |
|---|---|---|---|
routes/orders.ts cancelOrder() | POST /api/orders/:id/cancel | ユーザーのCookie | ステータス読み取り前に所有者チェックがない |
app/admin/actions.ts promoteUser ('use server') | RPC(表示可能なルートなし) | 管理者 | サーバーアクション — JSバンドルから列挙する |
共通の認証情報(各ロールとテナントのCookie/トークン)と、コードとランタイムを対応付ける設定(ベースURL、ルートのプレフィックス、Next.jsのサーバーアクションなどのフレームワークRPC、ルートとして表示されないtRPCプロシージャやGraphQLリゾルバー — owasp-audit A01と相互参照)を、一度だけ記録します。
双方向の優先順位付け — 双方からもう一方の方向性を決めます。
- コード -> 稼働中のアプリ。 ソースコードで見つかった問題(所有者チェック条件の欠落、チェックと処理の間の時間差、
redirect(req.query.next))から、稼働中のアプリでテストする具体的なエンドポイント、パラメーター、前提条件がわかります。手当たり次第のファジングを避け、すぐに実証に進めます。コードレビューで割り当てた深刻度に基づいて、稼働中のテストの順序を決めます。 - 稼働中のアプリ -> コード。 稼働中のアプリでの異常(403を想定したところで200が返る、タイミングに差がある、列名が漏れるエラーなど)によって、次に読むコードの優先順位が決まります。そのハンドラーを開き、ガードが実行されなかった理由をたどります。この組み合わせは一方向の引き継ぎではなく、ループとして扱います。一方で確認できた項目ごとに、もう一方の優先順位を改めて決めます。
このモードで得られた検出結果には、脆弱なコードの場所と、到達可能性を証明する稼働中のリクエスト/レスポンスの両方の証拠が含まれます。以下に対応する証拠を添えて、判定(confirmed/needs_validation/rejected)を記録します。
ツール
- Burp Suite Community(無料) — プロキシ、リピーター、イントルーダー(レート制限あり)、デコーダー
- Burp Suite Pro — スキャナー、高速なイントルーダー、セッション処理ルール
- OWASP ZAP — 無料の代替ツール、アクティブスキャナー、ファザー
- ffuf / feroxbuster / gobuster — ディレクトリとパラメーターのブルートフォース
- sqlmap — SQLi の悪用(慎重に使用してください。ノイズが多く、破壊的な場合があります。まずは
--risk=1 --level=1を指定してください) - dalfox / XSStrike — XSS スキャナー(誤検出が多いため、出発点として使用してください)
- nuclei — テンプレート駆動型の脆弱性スキャナー。テンプレート自体も非常に優れた学習資料です
- curl / httpie — 再現可能な手動リクエスト。見つけた内容はすべて、最終的に curl コマンドで再現できるようにしてください
概念実証 — 対になる証拠が必須です
単一のリクエスト/レスポンスでは、制御が失敗したことの証明にはなりません。アプリの通常の動作である可能性があるためです。**検証済みのすべての指摘には、攻撃リクエストとベースライン/対照の両方を含めます。テスト対象の変数だけが異なる、ほぼ同一の2つのリクエストを用意し、レスポンスの差から失敗した制御を特定できるようにしてください。**指摘の種類に応じてペアを作成します。
- **アクセス制御(IDOR/BOLA、BFLA、テナンシー):**所有者と非所有者。同じメソッド、パス、ボディ、ヘッダーを使った同一のリクエストで、セッショントークン/プリンシパルだけを変えます。所有者にはデータを含む 200 が返され、非所有者には 403/404 が返される必要があります。同じリソースに対する所有者の 200 と、非所有者の 200 を比較したものが証拠です。所有者と非所有者のペアがなければ、IDOR/BOLA の指摘は有効ではありません。
- **認証:**未認証と認証済み。有効なセッションあり/なしで、同じリクエストを送信します。
- **インジェクション/入力検証:**ペイロードと無害な入力。同じパラメーターに悪意のある入力と無害な対照入力を与え、ペイロードによってエラー、遅延、反射出力などの挙動が変化する一方、無害な入力では変化しないことを示します。
両方のやり取りを完全な形で(リクエスト+レスポンス)記録し、どちらが対照かを明記します。references/verification.md に従って判定を記載してください。判定は confirmed(対になった証拠で裏付けられている)、needs_validation(対照がまだ記録されていない。計画を記載する)、または rejected(対照によって問題がないと確認された)です。
検証してから記録する
まず findings.json を作成して検証し、その内容からレポートを生成します。references/findings-format.md に従ってください。JSON が記録の正本です。説明文によって判定、重大度、ブロッカー、実証された影響が変わることはありません。候補を確定する前に、すべて references/verification.md の反証プロセスにかけてください。検証担当は、取得したやり取りを読み直し、経路上で最も有力な制御を特定します。
ライブテストで得られるのはソースの行ではなく、証拠ファイルです。 取得した各やり取り(攻撃リクエスト/レスポンスと、それに対応するベースライン/制御)を、伏せ字にしたうえで <audit dir>/evidence/<name>.txt に保存し、location とすべての evidence[] エントリで、そのファイルと該当行を指定してください。実際のセッショントークン、パスワード、顧客レコードは絶対に保存しないでください。 Cookie は名前と末尾 4 文字だけを残して伏せ字にしてください。ホワイトボックスとブラックボックスを組み合わせるモードでは、ソースハンドラー(path/to/handler.ts:line)も trace[] で参照してください。
confirmed— 対応する証拠が成立しています。テスト対象の変数だけが異なる、ほぼ同一の 2 つのやり取りにより、失敗した制御を切り分けられています(所有者と非所有者、認証済みと未認証、ペイロードと無害な入力など。上記の「概念実証」を参照)。許可された対象に対して取得した対応する証拠は動的な証明となるため、reproduced: trueとし、confidenceはhighにできます。例:非所有者のGET /api/orders/42が注文内容を含む 200 を返し、所有者の同一リクエストも 200 を返す場合は BOLA です。証拠ファイルには両方のやり取りを保存します。needs_validation— 挙動は問題を示唆していますが、切り分けに必要な制御リクエストが取得されていないか、重要な事実がデプロイ環境に依存しています(WAF、ヘッダーを取り除くプロキシ、IdP の設定など)。ブロッカーを明記し、実行するコマンドをvalidation_planに記載してください。説明しただけで実行していないチェックはconfirmedではなくneeds_validationです。例:Host:を悪用したリンクを含むパスワードリセットメールを確認したものの、デプロイ済みのメーラーがそのヘッダーを信頼することを確認していない場合です。rejected— 名前が特定された制御が、特定の場所で到達可能なすべての経路において機能し、安全側に失敗しています。非所有者のリクエストは 403/404 を返し、ペイロードは拒否され、stateは検証されています。ruled_out_byには制御と、その実行場所(ミドルウェア、または証拠に記録されたデプロイ済みヘッダーセット)を指定します。
重大度を設定するのは confirmed レコードのみです。references/severity-calibration.md に従い、overall ≤ demonstrated impact としてください。ヘッダー不足、Cookie フラグ、オープンリダイレクト単独、機密性の高くない操作に対するクリックジャッキングを過大評価しないでください。具体的な影響をたどって示せない限り、これらはハードニング上の指摘です。安全でないデフォルト設定(許容的な CORS のデフォルト、ハードコードされたフォールバックシークレット)は、system_configuration 条件を付けて confidence: medium とし、confirmed にします。
ドメイン固有のフィールドは ext の下に記録します(任意のレコードに付けられるオブジェクトです)。
ext キー | 意味 | 例 |
|---|---|---|
wstg | WSTG テスト ID | WSTG-ATHZ-04 |
endpoint | テストしたライブエンドポイント | POST /api/orders/{id}/refund |
mode | 評価モード | black-box | grey-box | combined |
フィンガープリントの形式は web-pentest/<wstg-or-class>/<endpoint path or resource>/<parameter or operation> です。スラッシュで区切り、行番号は含めません(バリデーターは :<digits> を拒否するため、host:port はスラッシュを付けて記述してください)。例:web-pentest/WSTG-ATHZ-04/api/orders/id/refund、web-pentest/reflected-xss/search/q、web-pentest/tls/app.example.com/443/rc4-enabled。
findings.json をフィンガープリント順に並べて作成し、検証に合格するまで修正してください。
node references/validate-findings.mjs findings.json
任意で SARIF にエクスポートできます:node references/to-sarif.mjs findings.json -o findings.sarif。処置(Fixed/Deferred/Accepted Risk/Mitigated (platform/config)/Requires major migration)は後から、通常は finding-triage によって設定します。誤検知は処置ではなく、rejected レコードとして記録します。
出力形式
まず findings.json を作成して検証します。以下のレポートは JSON から生成され、JSON に記載されていない判定や重大度が変更されることはありません。
# Web Application Pentest Report
## Target: [name and scope] ## Methodology: OWASP WSTG ## Mode: [black-box | grey-box | combined]
## findings.json: [path] — validated: [yes] ## Engagement window: [start - end]
### Executive summary
[2-3 paragraphs — overall posture, top findings, business impact]
### Summary
- Confirmed: [N] (Critical [n] | High [n] | Medium [n] | Low [n]) Needs validation: [N] Rejected: [N]
### Confirmed findings
#### [SEVERITY] [Title] `fingerprint`
**Endpoint:** `METHOD /path` **WSTG / CWE:** [WSTG-XXX] / [CWE-XXX] **Confidence:** [medium | high (reproduced)]
**Code location (combined mode):** `path/to/handler.ts:line` — the source behind this endpoint
**What and why it matters:** [the trace, in a few lines]
**PoC — attack request/response:** [from `evidence/<name>.txt`]
**PoC — baseline/control:** [the paired request differing by one variable; label which is the control]
**Fix:** [concrete change] **Verify the fix:** [the check to re-run]
### Needs validation
| Finding | Blockers | Check for the owner to run |
|---|---|---|
### Hardening notes (not findings)
- [gap] — [why it isn't a finding] — [effort]
### Rejected (do not re-raise)
| Fingerprint | Ruled out by | Location |
|---|---|---|
### Out-of-scope / not tested
[Anything skipped, with reason]
### Prioritized remediation plan
1. [highest first — 30 / 60 / 90 day fixes]
重大度は confirmed の検出事項にのみ付与します。needs_validation には blockers[] と validation_plan が含まれ、重大度は付与されません。rejected には、該当する制御と場所を記載します。重大度の調整 に従って重大度を調整してください。
範囲
- 承認された範囲を厳守してください。明示的な確認なしに、隣接するシステム、サブドメイン、サードパーティのサービスをテストしてはいけません
- テストの頻度を制限してください。Burp Intruder で何時間も 100 req/s のリクエストを送信する行為は、DoS 攻撃と見分けがつきません
- 実際のユーザーデータを変更または破壊してはいけません。自分が所有するテストアカウント、または自分で作成したテストデータを使用してください
- アクティブな侵害の証拠(他者の Web シェル、進行中の情報流出など)を発見した場合は、テストを中止し、直ちにユーザーに通知してください
- 承認を得ていないシステムのテスト依頼は拒否してください
- 検出事項の武器化につながる依頼は拒否してください。概念実証は最低限必要な実証であり、実際に機能するエクスプロイトキットではありません
- DoS/可用性テストでは、メンテナンス時間帯を明記した書面による明示的な承認を得てください
- ソーシャルエンジニアリング/フィッシングテストでは、対象範囲にこれらを明示的に含める必要があります。既定ではペネトレーションテストの範囲に「含まれません」
参考資料
- OWASP Web Security Testing Guide (WSTG) — 標準的な手法
- OWASP Application Security Verification Standard (ASVS) — チェックリストのバージョン
- 共通の参考資料(ビルド時に
references/にコピー):findings-format.md、severity-calibration.md、verification.md - PTES (Penetration Testing Execution Standard)
- Bug Bounty Methodology (Jason Haddix / TBHM)
- PortSwigger Web Security Academy — 無料のトレーニングラボ
- HackTricks (book.hacktricks.xyz) — 手法の参考資料(最新の情報源で検証してください。HackTricks は品質にばらつきのあるコミュニティの知識を集約しています)