v4.15.4
Security
Fixes GHSA-vfp3-v2gw-7wfq: an encoded path separator (%2F or %5C) in a static file URL could bypass route-level middleware (e.g. authentication on a sibling route) and disclose static files. Both StaticDirectoryHandler (used by Static/StaticFS) and the Static middleware are affected. Backport of the v5 fix (#3016, released in v5.2.1). Thanks to @a-tt-om and @oran-gugu for reporting.
Make serving static file releated methods and middleware not unescape path by default - so how the way Router interprets paths and Static methods/middleware is consistent.
Given following situation:
// 0.
// given folder structure:
// private.txt
// public/
// public/index.html
// public/text.txt
// public/admin/private.txt
// 1. share `public/` folder contents from the server root. This folder actually contains subfolder `admin` which
// contents we want to forbid from downloading
e.Static("/", "public")
// 2. naively assume that everything under /admin folder is now forbidden
e.GET("/admin/*", func(c *Context) error {
return ErrForbidden
})
Then requests to /admin%2fprivate.txt would not be matched to GET /admin/* route (routing does not look unescaped path) and static file serving will use unescaped path to serve the file.
Note: this way of "guarding" subfolders will never work for for paths like /assets/../admin%2fprivate.txt which will path.Clean("/assets/../admin%2fprivate.txt") to /admin/private.txt and are servable if static file serving is configured to unescape paths.
If you want to guard routes - use middlewares on Static* methods and before Static middleware.
Breaking change / migration: If you serve files whose names contain URL-encoded characters (e.g., /hello%20world.txt → hello world.txt), you must now opt in:
e := echo.New()
e.EnablePathUnescapingStaticFiles = true // <-- enable old behavior
e.Static("/", "public")
for static middleware
e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
EnablePathUnescaping: true, // <-- enable old behavior
}))
Full Changelog: https://github.com/labstack/echo/compare/v4.15.3...v4.15.4
v5.2.1
Security
Make serving static file releated methods and middleware not unescape path by default - so how the way Router interprets paths and Static methods/middleware is consistent.
Given following situation:
// 0.
// given folder structure:
// private.txt
// public/
// public/index.html
// public/text.txt
// public/admin/private.txt
// 1. share `public/` folder contents from the server root. This folder actually contains subfolder `admin` which
// contents we want to forbid from downloading
e.Static("/", "public")
// 2. naively assume that everything under /admin folder is now forbidden
e.GET("/admin/*", func(c *Context) error {
return ErrForbidden
})
Then requests to /admin%2fprivate.txt would not be matched to GET /admin/* route (routing does not look unescaped path) and static file serving will use unescaped path to serve the file.
Note: this way of "guarding" subfolders will never work for for paths like /assets/../admin%2fprivate.txt which will path.Clean("/assets/../admin%2fprivate.txt") to /admin/private.txt and are servable if static file serving is configured to unescape paths.
If you want to guard routes - use middlewares on Static* methods and before Static middleware.
- revert PR #3009 changes to just disabling path escaping by default in static methods/middleware by @aldas in https://github.com/labstack/echo/pull/3016
Closes GHSA-vfp3-v2gw-7wfq more completely: the previous fix (#3009) rejected explicitly encoded separators at the handler level; this patch makes the no-unescape behavior the default so new configurations are safe without extra opt-out steps.
What changed: DisablePathUnescaping (on StaticConfig and StaticDirectoryHandlerConfig) is deprecated and replaced by EnablePathUnescaping (default false). Path unescaping is now opt-in.
What this protects: With EnablePathUnescaping: false (new default), encoded separators (%2F, %5C) are never decoded before routing or file lookup, so they cannot bypass route-level authentication or other middleware guards.
What this does NOT protect: Serving a directory with Static, StaticFS, or StaticDirectoryHandler exposes its entire subtree. Sibling routes are not a reliable ACL boundary — attach authorization middleware directly to the static mount, or serve sensitive sub-trees under separate guarded routes.
Breaking change / migration: If you serve files whose names contain URL-encoded characters (e.g., /hello%20world.txt → hello world.txt), you must now opt in:
// Static middleware
e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
EnablePathUnescaping: true, // only safe when NOT relying on route-based ACL guards
...
}))
// StaticDirectoryHandler
middleware.StaticDirectoryHandler(fs, &middleware.StaticDirectoryHandlerConfig{
EnablePathUnescaping: true,
})
Full Changelog: https://github.com/labstack/echo/compare/v5.2.0...v5.2.1
v4.15.3 - Static encoded-separator route bypass fix (GHSA-vfp3-v2gw-7wfq)
- fix(static): reject encoded path separators that bypass route-level middleware by @vishr in https://github.com/labstack/echo/pull/3011
Fixes GHSA-vfp3-v2gw-7wfq: an encoded path separator (%2F or %5C) in a static file URL could bypass route-level middleware (e.g. authentication on a sibling route) and disclose static files. Both StaticDirectoryHandler (used by Static/StaticFS) and the Static middleware are affected. Backport of the v5 fix (#3009, released in v5.2.0). Thanks to @a-tt-om and @oran-gugu for reporting.
Full Changelog: https://github.com/labstack/echo/compare/v4.15.2...v4.15.3
v5.2.0 - Static encoded-separator route bypass fix (GHSA-vfp3-v2gw-7wfq)
- fix(static): reject encoded path separators that bypass route-level middleware by @vishr in https://github.com/labstack/echo/pull/3009
- fix(middleware/static): don't double-unescape request path (#2599) by @vishr in https://github.com/labstack/echo/pull/3006
Fixes GHSA-vfp3-v2gw-7wfq: an encoded path separator (%2F or %5C) in a static file URL could bypass route-level middleware (e.g. authentication on a sibling route) and disclose static files. Both StaticDirectoryHandler/StaticFS and the Static middleware are affected. Thanks to @a-tt-om and @oran-gugu for reporting.
- feat(middleware): optional RateLimiterStoreContext for response headers (#2961) by @vishr in https://github.com/labstack/echo/pull/3007
- perf: optimize core hot paths (chain, context, binding, responses) by @vishr in https://github.com/labstack/echo/pull/3008
- fix(binder): include field name in bind conversion errors (#2629) by @vishr in https://github.com/labstack/echo/pull/3005
- fix(binder): serialize BindingError to structured JSON (#2771) by @vishr in https://github.com/labstack/echo/pull/3004
- fix(binder): MustUnixTime docs say time.Time, not time.Duration by @c-tonneslan in https://github.com/labstack/echo/pull/2988
- fix(middleware): reset ContentLength after gzip decompression by @shblue21 in https://github.com/labstack/echo/pull/3000
- fix(middleware/proxy): append RealIP to X-Forwarded-For for WebSocket requests by @kawaway in https://github.com/labstack/echo/pull/2994
- Fix proxy panic when balancer has no targets by @shblue21 in https://github.com/labstack/echo/pull/2977
- fix(middleware): correct documented KeyAuth KeyLookup default by @leestana01 in https://github.com/labstack/echo/pull/2992
- test: lock in v5 group route method-handling (405 + OPTIONS) by @vishr in https://github.com/labstack/echo/pull/3003
- docs: liveness signals in README + public ROADMAP by @vishr in https://github.com/labstack/echo/pull/3002
- Fix typos in CSRFConfig comments by @shblue21 in https://github.com/labstack/echo/pull/2979
- refactor: modernize code usage using gofix by @kumapower17 in https://github.com/labstack/echo/pull/2970
- refactor: replace Split in loops with more efficient SplitSeq by @box4wangjing in https://github.com/labstack/echo/pull/2969
- refactor: use the built-in max/min to simplify the code by @criciss in https://github.com/labstack/echo/pull/2966
- Update GitHub actions deps versions by @aldas in https://github.com/labstack/echo/pull/2971
- @criciss made their first contribution in https://github.com/labstack/echo/pull/2966
- @box4wangjing made their first contribution in https://github.com/labstack/echo/pull/2969
- @shblue21 made their first contribution in https://github.com/labstack/echo/pull/2977
- @c-tonneslan made their first contribution in https://github.com/labstack/echo/pull/2988
- @leestana01 made their first contribution in https://github.com/labstack/echo/pull/2992
- @kawaway made their first contribution in https://github.com/labstack/echo/pull/2994
Full Changelog: https://github.com/labstack/echo/compare/v5.1.1...v5.2.0
v5.1.1 - Context.Scheme() should validate header values
Security
Context.Scheme()should validate values taken from header by @aldas in https://github.com/labstack/echo/pull/2953
Thanks to @shblue21 for reporting this issue - Context.Scheme accepts malformed forwarded scheme values used by host redirects.
Enhancements
- Add golangci linter configuration by @aldas in https://github.com/labstack/echo/pull/2930
- Make StartConfig listener creation context-aware by @EricGusmao in https://github.com/labstack/echo/pull/2936
- fix(lint): resolve staticcheck issues and improve code quality by @itsllyaz in https://github.com/labstack/echo/pull/2941
- Context.Scheme should validate values taken from header by @aldas in https://github.com/labstack/echo/pull/2953
- chore: fix typos in httperror.go by @tisonkun in https://github.com/labstack/echo/pull/2958
- Context.Json should not unwrap response by @aldas in https://github.com/labstack/echo/pull/2964
- @EricGusmao made their first contribution in https://github.com/labstack/echo/pull/2936
- @itsllyaz made their first contribution in https://github.com/labstack/echo/pull/2941
- @tisonkun made their first contribution in https://github.com/labstack/echo/pull/2958
Full Changelog: https://github.com/labstack/echo/compare/v5.1.0...v5.1.1
v4.15.2 - Context.Scheme() header validation
Security
Context.Scheme()should validate values taken from header by @aldas in https://github.com/labstack/echo/pull/2962
Thanks to @shblue21 for reporting this issue.
Full Changelog: https://github.com/labstack/echo/compare/v4.15.1...v4.15.2
v5.1.0
This change does not break the API contract, but it does introduce breaking changes in logic/behavior. If your application is using c.RealIP() beware and read https://echo.labstack.com/docs/ip-address
In v5 the c.RealIP() will now return request.RemoteAddr unless e.IPExtractor has been configured. No potentially spoofable headers are used by default anymore.
Configure IPExtractor with proper trust options when you want to read IP from headers. See:
- https://pkg.go.dev/github.com/labstack/echo/v5#ExtractIPFromRealIPHeader
- https://pkg.go.dev/github.com/labstack/echo/v5#ExtractIPFromXFFHeader
v4 behavior can be restored with:
e := echo.New()
e.IPExtractor = echo.LegacyIPExtractor()
Related PR: Remove legacy IP extraction logic from context.RealIP method by @aldas in https://github.com/labstack/echo/pull/2933
- Add echo-opentelemetry to the README.md by @aldas in https://github.com/labstack/echo/pull/2908
- fix: correct spelling mistakes in comments and field name by @crawfordxx in https://github.com/labstack/echo/pull/2916
- Add https://github.com/labstack/echo-prometheus to the middleware list in README.md by @aldas in https://github.com/labstack/echo/pull/2919
- Add StartConfig.Listener so server with custom Listener is easier to create by @aldas in https://github.com/labstack/echo/pull/2920
- Fix rate limiter documentation for default burst value by @karesansui-u in https://github.com/labstack/echo/pull/2925
- Add doc comments to clarify usage of File related methods and leading slash handling by @aldas in https://github.com/labstack/echo/pull/2928
- Add NewDefaultFS function to help create filesystem that allows absolute paths by @aldas in https://github.com/labstack/echo/pull/2931
- Do not set http.Server.WriteTimeout in StartConfig by @aldas in https://github.com/labstack/echo/pull/2932
- Remove legacy IP extraction logic from context.RealIP method by @aldas in https://github.com/labstack/echo/pull/2933
- @crawfordxx made their first contribution in https://github.com/labstack/echo/pull/2916
- @karesansui-u made their first contribution in https://github.com/labstack/echo/pull/2925
Full Changelog: https://github.com/labstack/echo/compare/v5.0.4...v5.1.0
v4.15.1
- CSRF: support older token-based CSRF protection handler that want to render token into template by @aldas in https://github.com/labstack/echo/pull/2905
Full Changelog: https://github.com/labstack/echo/compare/v4.15.0...v4.15.1
v5.0.4 small fixes and improvements
- Remove unused import 'errors' from README example by @kumapower17 in https://github.com/labstack/echo/pull/2889
- Fix Graceful shutdown: after
http.Server.Servereturns we need to wait for graceful shutdown goroutine to finish by @aldas in https://github.com/labstack/echo/pull/2898 - Update location of oapi-codegen in README by @mromaszewicz in https://github.com/labstack/echo/pull/2896
- Add Go 1.26 to CI flow by @aldas in https://github.com/labstack/echo/pull/2899
- Add new function
echo.StatusCodeby @suwakei in https://github.com/labstack/echo/pull/2892 - CSRF: support older token-based CSRF protection handler that want to render token into template by @aldas in https://github.com/labstack/echo/pull/2894
- Add
echo.ResolveResponseStatusfunction to help middleware/handlers determine HTTP status code and echo.Response by @aldas in https://github.com/labstack/echo/pull/2900
- @mromaszewicz made their first contribution in https://github.com/labstack/echo/pull/2896
- @suwakei made their first contribution in https://github.com/labstack/echo/pull/2892
Full Changelog: https://github.com/labstack/echo/compare/v5.0.3...v5.0.4
v5.0.3 security (static middleware directory traversal under Windows)
Fix directory traversal vulnerability under Windows in Static middleware when default Echo filesystem is used. Reported by @shblue21 (https://github.com/labstack/echo/pull/2891).
This applies to cases when:
- Windows is used as OS
middleware.StaticConfig.Filesystemisnil(default)echo.Filesystemis has not been set explicitly (default)
Exposure is restricted to the active process working directory and its subfolders.
Full Changelog: https://github.com/labstack/echo/compare/v5.0.2...v5.0.3