FrankenPHPに認証回避の脆弱性、修正版1.13.0あり
FrankenPHPの1.13.0より前の版では、ドットを含むヘッダー名を使って、前段のプロキシが付けるはずの利用者名やクライアントIPを外部から偽装できます。FrankenPHPを使う組織は1.13.0へ更新し、アプリが信頼するヘッダーも点検してください。
急ぎ度: 早めに
CVSS v3は7.4、v4は9.1で、認証なしにネットワークから狙われ得る一方、ヘッダーを信頼する構成が前提であり、修正版1.13.0はすでに公開されています。
関係があるか
FrankenPHPでPHPアプリを動かし、リバースプロキシや認証プロキシが付けたヘッダー(利用者名・クライアントIP・スキームなど)をアプリが信頼している環境が対象です。Caddyを使うサーバーモードと、Caddyを使わずGoライブラリとして組み込むモードの両方が影響を受けます。
| 製品 | 影響を受ける版 | 修正版 |
|---|---|---|
| FrankenPHP(github.com/dunglas/frankenphp) | 1.13.0より前の版(GitHubの勧告では1.12.7以前) | 1.13.0 |
確かめ方
- 1稼働中のFrankenPHPの版が1.13.0未満かどうかを調べます。公式のDockerイメージを使っている場合は、デプロイ中のタグやダイジェストも見てください。
- 2Goのプロジェクトに組み込んでいる場合は、依存関係にあるgithub.com/dunglas/frankenphpの版を確認します。
- 3PHPアプリのコードで、$_SERVERのHTTP_X_で始まるキー(HTTP_X_FORWARDED_FORやHTTP_X_AUTHENTICATED_USERなど)を、認証・IP制限・https判定に使っていないか探します。使っていれば、実際に狙われ得る構成です。
何が起きたか
FrankenPHPが受け取ったHTTPヘッダーをPHPアプリに渡すとき、ドットで区切ったヘッダー名が、ハイフンで区切った正規のヘッダー名と同じものとして扱われます。そのため外部の利用者が、前段のプロキシだけが付けるはずのヘッダーを偽装できます。偽装できるのは利用者名、クライアントIP、通信のスキーム(httpかhttpsか)などです。
FrankenPHPは、ヘッダー名からPHPの$_SERVERのキーを作るときにハイフンだけをアンダースコアへ置き換えます。PHP本体はその後、キーに含まれるドットもアンダースコアに変えます。結果として、X-Forwarded-ForとX.Forwarded.Forは同じキーHTTP_X_FORWARDED_FORに行き着き、アプリ側では区別できません。
同梱のCaddyはアンダースコアを含むヘッダー名を捨てますが、ドットを含む名前は通します。フィルターが見る名前と、PHPが解釈する名前にずれがあることが原因です。CWE-290(なりすましによる認証回避)に分類されています。
攻撃の流れ
-
1
細工した名前で送信
攻撃者が、信頼ヘッダーのハイフンをドットに置き換えた名前でリクエストを送ります。
-
2
前段の対策を通過
前段のプロキシやCaddyは、ハイフン形やアンダースコア形しか除去せず、ドット形は素通りします。
-
3
同じキーに変換
FrankenPHPとPHPの変換を経て、正規ヘッダーと同じ$_SERVERのキーに入ります。
-
4
偽の値を信頼
アプリが偽の利用者名・IP・スキームを本物として扱い、認証や制限を誤って通します。
どう悪用されるか
アカウントや特別な権限は要らず、インターネット側などネットワーク越しにリクエストを送れれば狙えます。利用者の操作も要りません。前提は、PHPアプリまたは上流のプロキシが、$_SERVERにあるハイフン形のヘッダーを信頼していることです。前段でクライアントが送ったハイフン形を除去している構成でも、ドット形は除去の対象外になり得るため、この対策だけでは防げません。攻撃者は前段の設定を詳しく知らなくても、アプリが参照するヘッダー名が分かれば足ります。
被害は3つです。まず、利用者名やAPIキー相当のヘッダーを偽装されると、認証・認可を回避されます。次に、X-Forwarded-ForやX-Real-Ipを偽装されると、IPによるアクセス制限やレート制限、監査ログの記録が当てにならなくなります。最後に、X-Forwarded-Protoを偽装されると、https接続がhttpと見なされてダウングレードが起き、セキュアCookieの漏えいなどにつながります。CVSS v3のベクターでは、機密性と完全性への影響が高、可用性への影響はなしです。
今すぐやること
-
1
稼働中の版を確認する
サーバーモードなら、実行ファイルかコンテナ内で版を表示して、1.13.0未満かどうかを見ます。Goライブラリとして組み込んでいる場合は、依存関係の版を調べます。
frankenphp version -
2
1.13.0へ更新する
公式のDockerイメージを使っているなら、1.13.0以降のイメージで入れ替えます。Goに組み込んでいるアプリは、依存を更新して再ビルドし、再デプロイします。1.12.4への更新だけでは、ドット形が残るため不十分です。
-
3
アプリが信頼するヘッダーを洗い出す
認証・認可、IP制限、レート制限、https判定、監査ログなどで、$_SERVERのHTTP_X_で始まるキーを読んでいる箇所を探します。更新後も、外部から届く値をそのまま信頼しない設計かどうか見直してください。
grep -rn "HTTP_X_" /path/to/app -
4
侵害の跡を調べる
ヘッダー名を記録しているログがあれば、ドットを含むX.で始まる名前のリクエストがなかったか調べます。利用者名やIPを根拠にした操作に、不自然な点がないかも見ます。実際の攻撃の有無など最新の状況は、開発元の勧告で確認してください。
すぐ更新できないとき
すぐに更新できないときは、開発元が示した回避策ではなく一般的な緩和策ですが、FrankenPHPの前段のプロキシやWAFで、ドットを含むヘッダー名を持つリクエストを拒否または削除する方法があります。ハイフン形だけを除去している設定では不足します。あわせて、アプリが信頼するヘッダーの種類を最小限に絞ってください。ただし、これは更新までのつなぎです。
背景
FrankenPHPは、Goで書かれたPHPのアプリケーションサーバーです。Caddyの上で動くサーバーモードのほか、Goのライブラリとして組み込む使い方もできます。Caddyなどのリバースプロキシの背後に置かれることが多く、そのときアプリは、X-Forwarded-Forで元のIPを、認証プロキシが付けたヘッダーで利用者を知ります。つまり、このヘッダーが本物であることが前提の構成です。
PHPは、ヘッダー名を$_SERVERのキーにするとき、ドットやスペースをアンダースコアに変えます。別の表記が同じキーに合流するこの挙動は、フィルターを通した側が違う名前と判断すると、すり抜けの原因になります。GitHubの勧告は、同種の脆弱性としてTraefikのCVE-2026-88879を挙げています。FrankenPHPでは1.12.4でアンダースコア形に対処していましたが、ドット形は残っていたため、今回の修正に至りました。
よくある質問
Q.1.12.4以降に更新済みなら安全ですか。
Q.Caddyを使わず、Goライブラリとして組み込んでいる場合も対象ですか。
この記事は、取得元の公開情報をもとに、人が監修しながらAIを活用して執筆しています。執筆と監修は佐々木 航が担い、公開したあとも人が内容を確認しています。誤りに気づいたときは直し、記事の末尾に訂正の行を残します。対応を決める前に、下の原文と参照先も確認してください。
影響パッケージ
| パッケージ | 影響を受ける版 → 修正版 | |
|---|---|---|
| phpfrankenphp |
1.13.0 未満→ 1.13.0 |
1.13.0 |
原文の説明 取得元が公開している説明をそのまま載せています (多くは英語)。
FrankenPHP before 1.13.0 contains an authentication bypass vulnerability that allows unauthenticated attackers to spoof trusted headers by sending dot-form header names that PHP rewrites to underscores. Attackers can send headers like X.Authenticated.User or X.Forwarded.For that map to $_SERVER keys, spoofing user identity, client IP, or scheme.
出典: CVE.org (CVE Program・cvelistV5) NVD