Repository navigation
fix(deps): update module github.com/labstack/echo/v4 to v5 [security] - #539
Conversation
|
Important Review skippedBot user detected. To trigger a single review, invoke the
|
Drops echo v4 entirely, which clears the osv-scan findings (all fixed
in echo v5 >= 5.4.1), and moves echo-contrib to /v5 and echo-swagger
to /v2, the releases built on echo v5.
- handlers and middleware take *echo.Context
- server and metrics start through echo.StartConfig; the API server
shuts down gracefully on SIGINT/SIGTERM (echo v5 has no Shutdown)
- CORS: echo v5 refuses "*" with credentials, so a wildcard origin
now allows every origin without credentials instead of panicking
- validation errors keep their {"errors": {...}} body via a
self-marshalling status error (HTTPError.Message is now a string)
- batch Location header resolved through Router().Routes().Reverse
- tests set path values with SetPathValues; the SDK suite serves the
API through httptest instead of polling ListenerAddr
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- NewServer: an empty APIAllowedOrigins means any origin, as echo v4 defaulted to; echo v5 panics on an empty list, which broke every integration suite that builds a server without origins. - Dockerfile: build with CGO_ENABLED=0 and run on distroless/static-debian13. base-debian12's libssl3 carries HIGH and CRITICAL CVEs with no patched debian12 image yet (image-scan fails on main too), and the static image has no libssl or libc at all. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
Completed the echo v4 → v5 migration on top of Renovate's bump:
Verified locally: full integration suite ( 🤖 Generated with Claude Code |
Edited/Blocked NotificationRenovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR. You can manually request rebase by checking the rebase/retry box above. |
Echo v5's binders return a generic 400 ("Bad Request") wrapping the
decode error, where v4 put the detail in the message. api.NewError now
reports the wrapped error, so clients see e.g. "json: cannot unmarshal
number into Go struct field loginRequest.email of type string" again.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
gusfcarvalho
left a comment
There was a problem hiding this comment.
ccf-review: REQUEST_CHANGES
1 Must-fix.
ValidationErrorResponse keeps the {"errors": {field: [messages]}} 400
body that a map-valued HTTPError produced under echo v4. Assert the
status and body through echo's error handler, so a broken MarshalJSON
or StatusCode fails the test.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This PR contains the following updates:
v4.16.1→v5.0.0Echo: Reverse Proxy Forwards Attacker-Supplied X-Real-IP to Upstream by Default
CVE-2026-118651 / GHSA-99jh-6h7p-pp36
More information
Details
Summary
The Echo proxy middleware (
middleware/proxy.go) preserves an incomingX-Real-IPheader verbatim and forwards it to the upstream service whenEcho#IPExtractoris not configured — which is the default. Because any HTTP client can supply an arbitraryX-Real-IPvalue, an attacker can cause Echo to relay a spoofed client IP address to every upstream service behind the proxy, undermining upstream IP-based access controls, rate limiting, audit logging, and geo-restriction.Affected Component
middleware/proxy.go—ProxyConfig.ToMiddleware(), header-rewriting block (lines 351–355)Description
The header-preservation condition passes spoofed values to upstream
ProxyConfig.ToMiddleware()(proxy.go:338-405) prepares forwarding headers before dispatching each request. TheX-Real-IPrewrite logic at lines 351-355 reads:The condition is
TRUE(and therefore overwrites the header with a derived value) only when:req.Header.Get(echo.HeaderXRealIP) == ""; orIPExtractoris explicitly configured —c.Echo().IPExtractor != nilThe condition is
FALSE— and the header is left unchanged — when the request arrives with anX-Real-IPheader already set andIPExtractorisnil. BecauseEcho.New()leavesIPExtractorasnil, this is the out-of-the-box behaviour for every application that usesProxy()orProxyWithConfig()without extra configuration.An attacker who sets
X-Real-IPto any value causes Echo to forward exactly that value to the upstream, with no modification or validation.The comment in the source code confirms intent and the security gap
The inline comment at line 351-352 explicitly states:
The framework is aware that the default is insecure and retains it for backward compatibility. No warning appears in the
Proxy()orProxyWithConfig()documentation, so operators who follow the standard setup example receive a silently insecure proxy configuration.This is structurally identical to the documented insecurity in
ip.go:27:Full execution chain
GET /api/adminto the Echo proxy with headerX-Real-IP: 203.0.113.1(a trusted corporate IP)ToMiddleware()evaluates the guard at line 353: header is present (!= ""),IPExtractorisnil→ condition isFALSEreq.Header.Setis not called; the upstream request carries the original, attacker-suppliedX-Real-IP: 203.0.113.1X-Real-IPagainst its IP allowlist → finds203.0.113.1→ grants accessSecondary impact path (audit poisoning):
203.0.113.1rather than the real client IP, rendering audit trails and security analytics unreliable.Proof of Concept
The attacker-supplied IP
198.51.100.77arrives at the upstream unchanged.Impact
X-Real-IPX-Real-IPare bypassable (same class as the Echo rate-limiter finding, now on every upstream service)X-Real-IPrecord the attacker-chosen IP, not the real source — forensic trails are poisonedRecommended Remediation
Option 1: Always overwrite
X-Real-IPfrom an authoritative source (preferred)Remove the conditional that preserves the incoming header. Echo, as the edge proxy, is the only authoritative source for the real client IP:
For this to be safe, operators using
Proxy()should also configureIPExtractor:Option 2: Document and warn in
Proxy()godocIf backward compatibility must be preserved, add a prominent security warning to
Proxy()andProxyWithConfig():Credit
This vulnerability was discovered and reported by bugbunny.ai.
Patches
Fixed in
github.com/labstack/echo/v5v5.4.0: the Proxy middleware always setsX-Real-IPfromContext.RealIP(), which is the address of the direct peer unlessEcho#IPExtractoris configured.github.com/labstack/echo/v4is not changed, because v4'sContext.RealIP()withoutEcho#IPExtractortrustsX-Forwarded-ForandX-Real-IPby design, and changing that default would merge all clients behind a proxy into one rate-limit bucket.Workarounds
Configure
Echo#IPExtractorfor your deployment, for examplee.IPExtractor = echo.ExtractIPDirect()when there is no proxy in front of the application, orecho.ExtractIPFromXFFHeader()/echo.ExtractIPFromRealIPHeader()(withTrustIPRangefor proxies on public addresses). With an extractor set, the Proxy middleware overwritesX-Real-IPin v4 too.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Release Notes
labstack/echo (github.com/labstack/echo/v4)
v5.0.0Compare Source
Echo
v5is maintenance release with major breaking changesContextis now struct instead of interface and we can add method to it in the future in minor versions.Routerinterface for possible new routing implementations.log/sloginstead.Upgrade notes and
v4support:v4is supported with security* updates and bug fixes until 2026-12-31v5API changes will be addressed, even if this violates semantic versioning.See API_CHANGES_V5.md for public API changes between
v4andv5, notes on upgrading.Upgrading TLDR:
If you are using Linux you can migrate easier parts like that:
macOS
or in your favorite IDE
Replace all:
echo.Context->*echo.Contextecho/v4->echo/v5This should solve most of the issues. Probably the hardest part is updating all the tests.
v4.16.2Compare Source
Fixes
Basicauthentication scheme. Malformed schemes such asBasicXno longer invoke the credential validator. Valid credentials are still required. This adapts the v5 correction to v4's single-header API. #3188, backport of #3138, with credit to @Shubham-Padkonde.CSRFConfig.TokenLookupconfigurations that produce no recognized token extractors. Middleware creation now returns an error instead of silently skipping token validation. #3149, thanks to @orinnz; backport of the existing v5 validation in #3067.Upgrade
Go 1.26 remains the module minimum. Build deployments with a supported, patched Go toolchain. No dependencies or public APIs changed in this release. Applications with invalid CSRF token lookup configuration will now fail middleware creation at startup.
The security fixes, bounded BodyDump and Decompress defaults, and dependency updates released in v4.16.1 are included unchanged. Their first patched version remains v4.16.1.
Validation: the fixes passed regression tests and upstream race-test, lint and benchmark checks on Linux, macOS and Windows with Go 1.26 and 1.27. The new regressions were also verified to fail without the fixes; local race tests and vet passed.
Full changelog: labstack/echo@v4.16.1...v4.16.2
Configuration
📅 Schedule: (in timezone UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR has been generated by Mend Renovate CLI.