EchoのWebSocketプロキシ認証バイパス(GHSA-cxf3-9h37-7rr8)
EchoのProxyミドルウェアを通るWebSocketアップグレードが拒否されても、上流への生のトンネルが開いたままになります。同じ接続上の後続HTTPリクエストはEchoのルーターとルートミドルウェアを通らず、Echoのルートだけで適用する認証を回避します。GitHub Advisory Databaseはクリティカル、CVSS v4.0 9.1、Echo自身は中程度と評価しています。2026年10月22日公開のv4.16.1とv5.4.1で修正済みです。公開PoCや実環境での悪用は確認されていません。
要点
- クライアントがEchoのProxyミドルウェアで処理されるルートにWebSocketアップグレードを要求すると、旧バージョンはクライアント接続をハイジャックして上流に直接接続し、上流のレスポンスを読まずに双方向でバイトをコピーしていました。上流が通常のHTTPレスポンスでアップグレードを拒否し、接続を維持すると、トンネルは開いたままになりました。
- 攻撃者が同じ接続で送った後続のHTTPリクエストは上流に直接転送され、多くのアプリケーションが認証や認可を適用するEchoのルーターとルートミドルウェアには届きませんでした。
- GitHub Advisory Databaseのレビューはクリティカル、CVSS v4.0 9.1と評価しています。Echoのメンテナーは独自のアドバイザリで中程度としています。CWE-444とCWE-863が記録されています。2026年10月24日時点でKEVカタログに登録されておらず、公開エクスプロイトや実環境での悪用も確認されていません。
- v4系はv4.16.1以降、v5系はv5.4.1以降にアップグレードしてください。それまでは、プロキシルートに届く前にWebSocketアップグレードリクエストを拒否するか、上流で独立した認可を適用してください。
影響を受けるか
以下の条件をすべて満たす場合に影響を受けます。
- アプリケーションまたはAPIゲートウェイがEchoを使用しており、モジュール
github.com/labstack/echo/v4が4.16.0以前、またはgithub.com/labstack/echo/v5が5.4.0以前 - ルートまたはグループがEcho組み込みのProxyミドルウェア(
middleware.Proxy、ProxyWithConfig、バランサーヘルパー)を使い、Echoが上流にリクエストを転送 - クライアントがそのプロキシルートに到達でき、WebSocketアップグレードを要求可能
- 上流のリソースのセキュリティチェックが、上流自体ではなくEchoのルートやミドルウェアに存在
Proxyミドルウェアを配置していなければ影響はありません。通常のEchoルーティング、認証ミドルウェア、静的ファイル配信などは、この不具合の対象外です。また、このバイパスで到達できるホストはプロキシに設定された上流だけで、上流自体の認証は破られません。pkg.go.devではgithub.com/labstack/echo/v4をインポートする32,160のパッケージが掲載されています(2026年10月24日確認)。危険にさらされるのは、そのうちProxyミドルウェアを配置するものだけです。
| ブランチ | 影響を受けるバージョン | 修正バージョン |
|---|---|---|
| echo/v4 | >= 4.0.0, < 4.16.1 | 4.16.1 |
| echo/v5 | >= 5.0.0, < 5.4.1 | 5.4.1 |
アドバイザリによると、v4.15.0、v4.16.0、v5.0.0、v5.4.0で実行時の再現が確認されています。ソース履歴からは、検証の欠如がv4.0.0までさかのぼることもわかっています。
モジュールとバージョンを確認してください。
go list -m github.com/labstack/echo/v4
go list -m all | grep labstack/echo
govulncheck ./...
# In a Docker image that has the module cache
docker run --rm <image> sh -c "go list -m github.com/labstack/echo/v4 2>/dev/null || grep labstack/echo /go.mod"
# Directly in the build files
grep -rn "labstack/echo" go.mod go.sum
govulncheckは、影響を受けるバージョンのモジュールがビルドグラフに含まれていれば、このアドバイザリに基づいて警告します。
修正方法
モジュールをアップグレードします。
go get github.com/labstack/echo/v4@v4.16.1
# or, on the v5 line
go get github.com/labstack/echo/v5@v5.4.1
go mod tidy
v4.16.1のリリース要件に注意してください。修正されたgolang.org/x/の依存関係はGo 1.26以降を必要とするため、ビルドツールチェーンも更新が必要な場合があります。2026年10月23日公開のv4.16.2など、その後のパッチリリースには同じ修正と追加対応が含まれます。その系統の最新パッチに固定する方法でも対応できます。
すぐにアップグレードできない場合、アドバイザリには2つの回避策と、回避策にならない重要な対応が記載されています。
- 影響を受けるプロキシルートに届く前にWebSocketアップグレードリクエストを拒否する。プロキシの前に小さなミドルウェアを配置し、アップグレード試行に通常のエラーを返せば、入口を閉じられます。
- 上流のすべてのリソースで独立した認可を適用する。Echoのルート層を回避したリクエストにも、実際のチェックを適用できます。
- 上流で101以外のレスポンスを返すだけでは対応できません。まさにその拒否によって、脆弱なトンネルが開いたままになるためです。
制約は明確です。最初の回避策はWebSocketのプロキシ機能を完全に無効化します。2つ目は独自のチェックがあるリソースだけを保護するため、アプリケーション側でリソースを一つずつ検証する必要があります。
根本原因
EchoのProxyミドルウェアは、上流がアップグレードを受け入れたか確認する前にWebSocketリクエスト用の生のバイトトンネルを選択していたため、上流が拒否した後もトンネルが動作し続けました。
v4.16.0のmiddleware/proxy.go、391行目の振り分け処理です。
// Proxy
switch {
case c.IsWebSocket():
proxyRaw(tgt, c, config).ServeHTTP(res, req)
default: // even SSE requests
proxyHTTP(tgt, c, config).ServeHTTP(res, req)
}
問題はproxyRaw(134行目)です。クライアント接続をハイジャックし、上流に接続して双方向にバイトをコピーします。
in, _, err := c.Response().Hijack()
...
out, err := dialFunc(c.Request().Context(), "tcp", t.URL.Host)
...
// Write header
err = r.Write(out)
...
errCh := make(chan error, 2)
cp := func(dst io.Writer, src io.Reader) {
_, copyErr := io.Copy(dst, src)
errCh <- copyErr
}
go cp(out, in)
go cp(in, out)
欠けている処理に注目してください。r.Write(out)がアップグレードリクエストを送り、2つのio.Copyループがバイトを転送し続けますが、アップグレードリクエストに対する上流のHTTPレスポンスを読み取る処理がありません。また、上流が101 Switching Protocolsを返したことをトンネルの条件にしていません。WebSocket以外の分岐であるproxyHTTPは、上流のレスポンスを検証するGoのhttputil.NewSingleHostReverseProxyに処理を委譲しており、対照的です。
結果はこの構造から直接生じます。クライアント接続がハイジャックされると、その接続に対するEchoのリクエスト処理は終了します。上流がアップグレード試行に通常のHTTPレスポンスを返し、重要な条件としてHTTP keep-aliveでTCP接続を維持すると、生のコピーループは転送を続けます。クライアントは上流から拒否レスポンスのバイトを受け取った後、同じ接続上で別の通常のHTTPリクエストを送れます。cp(out, in)はそれをそのまま上流へ書き込み、上流の返信も同様に戻ります。Echoから見れば接続はハイジャック時点でHTTP処理の外に出ているため、これらのリクエストはEchoのルーター、ルートミドルウェア、レート制限、ルートレベルの認証をすべて回避します。
悪用の仕組み
公開の概念実証(PoC)はありません。報告者@Duodumへの謝辞とともにメンテナーのvishrが公開したアドバイザリは、仕組みを説明し、v4.15.0、v4.16.0、v5.0.0、v5.4.0でメンテナーが実行時に再現したと記載しています。説明されている攻撃経路は次のとおりです。
- 攻撃者がProxyミドルウェアを使うEchoのルートに接続し、WebSocketアップグレードヘッダーを含むHTTPリクエストを送ります。
- Echoはクライアント側の接続をハイジャックして対応し、設定された上流にアップグレードを要求します。
- 上流はそのパスでWebSocketを受け付けていないか、要求を拒否し、維持した接続上で200や404など通常のHTTPレスポンスを返します。
- 生のコピーループが動き続けるため、攻撃者はEchoが検査しない上流への持続的なパイプを保持します。
- 攻撃者はそのパイプに通常のHTTPリクエストを書き込みます。各リクエストがバイト単位でそのまま上流に転送され、レスポンスも直接戻ります。
- 上流が独自の認可チェックなしで提供するパスやリソースに到達できます。同じリクエストにEchoのルートが認証を要求していても同様です。
この連鎖の条件はすべて重要です。アクセス可能なプロキシルート、拒否しても接続を閉じない上流、プロキシ入口のチェックを信頼する上流リソースが必要です。
攻撃フロー
WebSocketアップグレードを送信"] --> B["EchoがproxyRawを選び
クライアント接続をハイジャック"] B --> C["生のリクエストを上流へ書き込み
上流のレスポンスは読み取らない"] C --> D["上流が通常のHTTPで拒否し
接続を維持"] D --> E["アップグレード検証がなく
トンネルがコピーを継続"] E --> F["攻撃者が同じ接続上で
通常のHTTPを送信"] F --> G["Echoのルーターとミドルウェアを通らず
そのまま上流へ転送"] G --> H["保護されていない上流リソースを提供
ルート認証を回避"]
攻撃者に可能なこと
攻撃者は未認証クライアントの権限で、Echoのルート層が保護するはずだった設定済み上流のリソースに到達します。影響は、独立したチェックなしでリソースが公開する内容に応じます。内部エンドポイント、未認証サービス、プロキシのルートミドルウェアで守られていると想定された管理パネルなどです。アドバイザリでは、次の制約が示されています。
- 任意のホストへのプロキシはできません。トンネルの接続先は設定済みの上流だけであり、オープンプロキシにはなりません。
- 上流自体に認証がある場合、その認証は引き続き適用されます。
- アクセス可能なプロキシルートが必要で、上流は接続を閉じずにアップグレードを拒否する必要があります。
- ルートへのネットワークアクセス以外に、認証、ユーザー操作、特権的な位置は必要ありません。
この制約を踏まえると、実際に危険があるのは、Echoで認証を適用し、その背後の上流が到達不能であることや認証なしで動作することに依存する構成です。2つの重大度評価は、この点で見解が分かれています。
修正が機能する理由
パッチはproxyRawを完全に削除し、WebSocketかどうかにかかわらず、すべてのリクエストをGoのリバースプロキシ経由にします。v4の修正コミットからの抜粋です。
- // Proxy
- switch {
- case c.IsWebSocket():
- proxyRaw(tgt, c, config).ServeHTTP(res, req)
- default: // even SSE requests
- proxyHTTP(tgt, c, config).ServeHTTP(res, req)
- }
+ // ReverseProxy validates upgrades and closes both sides when a tunnel ends.
+ proxyHTTP(tgt, c, config).ServeHTTP(res, req)
proxyHTTPは、接続先のスキームwsとwssをhttpとhttpsに正規化し、httputil.NewSingleHostReverseProxyに委譲するようになりました。この処理は切り替えを確定する前に上流のレスポンスを読みます。有効な101 Switching Protocolsの応答後にだけトンネルへ移行し、拒否されたアップグレードは通常のHTTPレスポンスとして中継します。リリースノートにあるとおり、拒否されたアップグレードによってEchoのルーティングとルートミドルウェアを回避する生のトンネルが残ることはなくなりました。
この問題に対するミドルウェア層の修正は完全です。Proxyには、アップグレードが受け入れられずにトンネルへ移行する経路がなくなりました。また、どちらかが切断した場合には両側を閉じる修正も含まれ、これは関連アドバイザリGHSA-9969-6676-49h5に対応しています。ただし、この修正が上流に認証を追加するわけではありません。独立したチェックがなかったリソースは、正しくルーティングされた認証済みトラフィックに対して、以前と同じようにアクセス可能なままです。
侵害されたかを確認する方法
この問題の侵害指標は公開されておらず、Echoも掲載していません。攻撃者がログに残すリクエストパターンには特徴があるため、自社のログで確認できます。
Connection: UpgradeとUpgrade: websocketというWebSocketアップグレードヘッダーを付けたプロキシルートへの接続が、上流の101以外のレスポンスの後もHTTPトラフィックを運んでいる。- 単一の長時間接続から複数の通常のHTTPリクエストが上流に記録されている一方、前段のプロキシには同じトラフィックに対応するルーティング済みリクエストが記録されていない。
- Echoのルート層で制限するはずのリソースにリクエストが届いたことを示す上流のアクセス記録。
上流のログにリソースパスが、プロキシのログにルーティングされたパスが含まれている場合、同じクライアント接続に対する両者の不一致が調査の手掛かりになります。
経緯
- 2026-10-22:Echoが修正を含むv4.16.1とv5.4.1をリリース。メンテナーのvishrがGHSA-cxf3-9h37-7rr8を公開し、中程度と評価して@Duodumに謝辞を記載。
- 2026-10-22:v4.16.1とv5.4.1のリリースノートが、新しい検証処理と関連するセキュリティ修正を説明。
- 2026-10-23:GitHub Advisory Databaseがレビューしたアドバイザリをクリティカル、CVSS v4.0 9.1として公開し、CWE-444とCWE-863を記載。Echoはv4系の追加強化を含むv4.16.2を公開。
- 2026-10-24:本記事を公開。
よくある質問
重大度評価が異なる理由は?
GitHub Advisory Databaseのレビューは、攻撃要件が存在するとしたうえでCVSS v4.0 9.1、クリティカルと評価しています。一方、Echo自身のアドバイザリページでは、スコアを公開せず中程度としています。この違いは前提条件に関係します。攻撃には、プロキシルート、接続を閉じずに拒否する上流、独自のチェックがない上流リソースが必要です。これらを満たす場合、アドバイザリデータベースはルートレベルのセキュリティ境界が完全に破られると評価しており、それが修正の緊急性に反映されています。
サーバーがオープンプロキシになる可能性は?
ありません。トンネルはProxyミドルウェアで転送先に設定された上流だけに接続します。アドバイザリも、任意ホストへのプロキシを可能にする問題ではないと明記しています。危険にさらされるのは自社の上流リソースであり、サードパーティの宛先ではありません。
上流自体の認証も回避するか?
いいえ。上流自体の認証機構は以前と同じように機能します。回避されるのはEchoのルートとミドルウェアだけに存在する制御です。そのため、アドバイザリの回避策では上流自体で認可を行うよう勧めています。
EchoのProxyミドルウェアを使っていないサービスも影響を受けるか?
いいえ。この不具合はProxyミドルウェアの生のWebSocket経路にあります。ルーティング、静的ファイル、独自ハンドラーにEchoを使っていても、プロキシを配置しないアプリケーションはこのコードパスを使用しません。
v4.16.1とv5.4.1の修正に同梱されたものは?
両リリースには、データベースで重大度が高とされた3つの関連アドバイザリへの修正も含まれます。クライアント切断後もWebSocketプロキシ接続が維持されるGHSA-9969-6676-49h5、v4のDecompressミドルウェアでgzip展開量が制限されないGHSA-h7wr-284f-5qvc、v4のBodyDumpによる取得量が制限されないGHSA-f48p-c9v4-h7xvです。WebSocketの修正のためにアップグレードすれば、これらにも対応できます。
OstorlabでGoサービスとコンテナーイメージをスキャンし、含まれるEchoモジュールのバージョンを特定して、v4.16.0またはv5.4.0以前のままの構成を検出してください。
参考資料
- EchoのアドバイザリGHSA-cxf3-9h37-7rr8。ベンダーの評価は中程度。2026年10月22日公開、@Duodumに謝辞:https://github.com/labstack/echo/security/advisories/GHSA-cxf3-9h37-7rr8 (2026年10月24日確認)
- GitHub Advisory Databaseのレビュー。クリティカル、CVSS v4.0 9.1、CWE-444、CWE-863:https://github.com/advisories/GHSA-cxf3-9h37-7rr8 (2026年10月24日確認)
- v4系の修正コミット3fb0ba7e:https://github.com/labstack/echo/commit/3fb0ba7e17645bb65611e2a08875d0a2f65c421f (2026年10月24日確認)
- v5系の修正コミット1855061e:https://github.com/labstack/echo/commit/1855061e572ae9dab727c811d8382f266bfbaaec (2026年10月24日確認)
- 2026年10月22日公開のv4.16.1リリースノート。Go 1.26要件と関連修正を含む:https://github.com/labstack/echo/releases/tag/v4.16.1 (2026年10月24日確認)
- 2026年10月22日公開のv5.4.1リリースノート:https://github.com/labstack/echo/releases/tag/v5.4.1 (2026年10月24日確認)
- 2026年10月23日公開のv4.16.2リリースノート:https://github.com/labstack/echo/releases/tag/v4.16.2 (2026年10月24日確認)
- v4.16.0の脆弱なproxy.goソース:https://raw.githubusercontent.com/labstack/echo/v4.16.0/middleware/proxy.go (2026年10月24日確認)
- pkg.go.devにおけるecho/v4のインポート数、32,160パッケージ:https://pkg.go.dev/github.com/labstack/echo/v4 (2026年10月24日確認)
- 関連アドバイザリGHSA-9969-6676-49h5:https://github.com/advisories/GHSA-9969-6676-49h5 (2026年10月24日確認)
- 関連アドバイザリGHSA-h7wr-284f-5qvc:https://github.com/advisories/GHSA-h7wr-284f-5qvc (2026年10月24日確認)
- 関連アドバイザリGHSA-f48p-c9v4-h7xv:https://github.com/advisories/GHSA-f48p-c9v4-h7xv (2026年10月24日確認)