Go の Web フレームワーク Echo に、グローバルに設定した middleware.Static が認可を抜けてファイルを配信する脆弱性 CVE-2026-118650 が公表されました。/admin%2Fsecret.txt のように %2F(符号化したスラッシュ) を含むパスが、/admin/* を守るミドルウェアを素通りして、保護対象のファイルを返しうる、というものです。深刻度は CVSS v3.1 で 7.5(High)、影響は情報開示でコード実行ではありません。修正は 5.4.0 と 4.16.0 です。この記事は、なぜ guard を抜けるのか・自分の構成が対象か・どう直すかを、CVE 公式記録と Echo 公式の勧告・CHANGELOG で裏を取って整理します。依存ライブラリの欠陥がアプリの守りを崩す全体像はサプライチェーン攻撃とは|手口・防御ツール比較・npm 12の新機構まで実践解説に、認可の確認が抜ける類型はIDOR とは|認可チェックの抜けを塞ぐ設計、パスの取り違えで本来触れない場所に届く問題はパストラバーサルの仕組みと防ぎ方にまとめています。
WHAT HAPPENED時系列
Echo の 静的配信が guard を抜ける経緯
-
9/27
Echo v5.4.0 を公開(Security リリース)
middleware.Static が『ルータが照合した形』でファイルを解決するよう修正。CVE-2026-118650(GHSA-375p-5qhx-8wq4)
-
同時
前メジャーへバックポート v4.16.0
同じ修正を 4.x 系にも提供。あわせて .. / 空セグメントの遮断、リダイレクトの制御文字対策も入った
-
—
GitHub が CVE を採番・公開
CVSS v3.1 7.5 High(AV:N/AC:L/PR:N/UI:N・機密性 High)。CWE-177(URL符号化の不適切処理)+CWE-863(不正な認可)
-
10/29
GitHub Advisory DB 掲載で検知
当サイトの sec-watch が候補として検知。影響 >=5.0.0 <5.4.0 と <4.16.0
30秒でわかる
- ・何が起きたか:グローバルな middleware.Static が guard を抜ける。
/admin%2Fsecret.txtが/admin/*の保護を通らずに配信されうる - ・原因:ルータは %2F を復号せず照合、静的ハンドラは復号して root 配下を解決、という食い違い(CWE-177+CWE-863)
- ・深刻度:CVSS 7.5(High)・情報開示。影響は 5.0.0〜5.3.x と 4.16.0 未満。コード実行ではない
- ・対応:5.4.0 / 4.16.0 以上へ。root の外へ保護ファイルを出す/
EnablePathUnescapingとルート制御を併用しない
まず対象かを確かめます。自分のプロジェクトで使っている echo の版を見ます。
go list -m all | grep labstack/echo
echo/v5 が 5.4.0 未満、または echo/v4 が 4.16.0 未満なら対象の版です。これは侵害の判定ではなく、修正の入った版かの確認です。
いつ、何が起きたのか
発端は、Echo のルータと静的ファイル配信が、同じ要求パスを別の形で解釈していたことです。2026年9月27日の Echo v5.4.0(Security リリース)で修正され、前メジャー向けに 4.16.0 がバックポートされました。CVE は GitHub が採番し、CVSS v3.1 で 7.5(High)、CWE は 177(URL 符号化の不適切な処理) と 863(不正な認可) です。
middleware.Static は、指定したディレクトリ(root)の下のファイルをそのまま配信する Echo のミドルウェアです。これを e.Use(...) でアプリ全体にグローバルに設定すると、どのルートにも一致しなかった要求が最後にここへ届き、root 配下のファイルを探します。問題は、ここへ来るまでに通るはずの「ルート単位の guard」を、符号化したパスがすり抜ける経路があったことです。
同じリリースでは、関連する2件も直っています。.. や空のセグメントを含むパス(/assets/../admin/secret.txt など)を復号後も配信しないようにした修正(GHSA-3pmx-cf9f-34xr)と、リダイレクト先のパスの制御文字を符号化する修正(GHSA-v753-g4cw-jm48)です。いずれも「パスの見え方の違いで本来届かない場所に届く」類型で、まとめて塞がれました。
なぜ guard を抜けるのか
WHY IT BYPASSESルータと静的配信の食い違い
同じパスを別の形で読んでいた
ルータの見方
静的ハンドラの見方
ルータの見方: /admin%2Fsecret.txt を符号化のまま照合
静的ハンドラの見方: URL.Path を復号して admin/secret.txt に
ルータの見方: /admin/* の guard ルートに一致しない
静的ハンドラの見方: root 配下の該当ファイルとして解決してしまう
ルータの見方: → guard が働かない(素通り)
静的ハンドラの見方: → 保護されるはずのファイルを配信
Echo のルータは、既定で %2F を含むパスを符号化のまま照合します。そのため /admin%2Fsecret.txt は、文字列として /admin/* のパターンに一致せず、そのルートに付けた認可ミドルウェア(guard)は呼ばれません。要求はどのルートにも一致しないまま、最後にグローバルな middleware.Static へ届きます。ここで静的ハンドラは URL.Path を復号して admin/secret.txt を root 配下から探すため、guard を通らずにファイルが配信されえます。CVE の説明は、グローバル mount の構成で guard が対象パスを守っているかについて勧告の記述に内部的な不整合がある、とも注記していますが、要点はルータと静的配信でパスの形がずれていたことです。
修正版は、この食い違いを解消します。既定設定では、ルータが照合したのと同じ形でファイルを解決するため、/admin%2Fsecret.txt や /%61dmin/secret.txt(a を符号化)が guard 下のファイルに届かなくなります。ここでは攻撃に使える具体的な手順(PoC)は示しません。
この類型は Echo だけの話ではなく、パスの正規化を複数の層で別々に行うと起きがちなものです。ルート照合・認可・ファイル解決が、それぞれ「符号化したまま」「復号してから」とばらばらにパスを見ると、どこかの層で守ったつもりが別の層で抜けます。守りの判断(認可)と、実際にファイルを取り出す処理が、同じ正規化を通した同じ文字列を見ているか——これが、フレームワークを問わず効く確認の勘所です。今回の修正は、まさにその一致を既定で保証する方向の変更です。
何を、どの版へ上げるか
対応は版を上げることが中心です。go.mod のモジュールパス(echo/v5 か echo/v4)に合わせて上げます。上げ方は、echo/v5 なら go get github.com/labstack/echo/[email protected]、echo/v4 なら go get github.com/labstack/echo/[email protected] のように版を指定して取得し、go mod tidy で依存を整え、ビルドして配布し直します。CI では版を固定し、go.sum の差分をレビューに含めると、意図した版へ上がったかを確認できます。複数サービスで echo を使っているなら、同じ経路の構成が他にないかもあわせて見ます。
| 対象 | 影響する版 | 修正版 |
|---|---|---|
github.com/labstack/echo/v5 |
5.0.0 以上 5.4.0 未満 | 5.4.0 以上(執筆時の最新 5.4.2) |
github.com/labstack/echo/v4 |
4.16.0 未満 | 4.16.0 以上 |
上げる前に、挙動の変化を確認してください。修正版では、/assets//app.js のような二重スラッシュやドットのセグメントを含むパスは 404 になります(HTML5 モードの index は従来どおり)。また StaticConfig.EnablePathUnescaping を有効にすると符号化スラッシュは依然として復号されるため、この設定をルートベースのアクセス制御と組み合わせないでください。これは公式の CHANGELOG が明記している注意点です。
自分の構成を確認する
対象の版だったら、次にグローバルな静的配信を使っているかを見ます。アプリ全体に middleware.Static を付けている箇所を探します。
grep -rn "middleware.Static" .
e.Use(middleware.Static(...)) のようにグローバルに付けていて、その root の下に、ルート単位の認可で守っているパスが含まれているなら、対象の構成です。実際に自分の環境が配信してしまうかは、保護しているはずのパスを符号化して、ステージング環境で確かめます(悪用用のデータではなく、挙動の確認です)。
curl -i "https://<your-staging-host>/admin%2Fsecret.txt"
修正前は guard を通らずファイルが返り、修正後は 404 などで届かなくなります。確認できるのは「修正・設定が効いているか」で、侵害の有無ではありません。
影響範囲
影響を受けるのは、グローバルに mount した middleware.Static の配信 root の下に、ルート単位のミドルウェアで守っているパスが同居しているアプリです。逆に、Echo.Static / Group.Static を guard の内側で使っている構成や、保護対象を root の外に置いている構成は、この経路の直接の対象ではありません。結果は情報開示(本来読めないファイルが読まれる)で、コード実行や改ざんではありません。
深刻度は CVSS v3.1 で 7.5(High)で、ネットワーク越しに認証なしで成立しうる一方、影響は機密性に限られます。優先度は、配信 root に何が同居しているかで決まります。秘密鍵・設定ファイル・個人情報などが root 配下にあるなら高く見積もり、静的アセットしか置いていないなら相対的に下がります。
よくある危ない同居の例としては、public/ を丸ごとルート直下から配信していて(e.Static("/", "public"))、その中に管理画面向けのファイルや、ビルドの成果物に紛れた .env・設定テンプレート・バックアップが残っている、といった構成があります。こうした「静的配信の root に、本来は認可の内側に置くべきものが混ざっている」状態ほど、今回の経路の影響が大きくなります。棚卸しとして、配信 root の下に guard 対象や秘密が無いかを一度見ておくと、版を上げたあとも安全側に倒せます。
いずれにせよ修正は版を上げるだけで軽いので、待つ理由はありません。保護したいファイルは、恒久的に配信 root の外へ出しておくのが安全で、これは版を上げたあとの運用ルールとしても有効です。





