-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Fix CORS header duplication in proxy chains #2902
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -193,7 +193,25 @@ func (config CORSConfig) ToMiddleware() (echo.MiddlewareFunc, error) { | |
| res := c.Response() | ||
| origin := req.Header.Get(echo.HeaderOrigin) | ||
|
|
||
| res.Header().Add(echo.HeaderVary, echo.HeaderOrigin) | ||
| // Check if CORS headers already exist (e.g., from a proxied upstream service) | ||
| // to avoid duplication in proxy chains | ||
| if res.Header().Get(echo.HeaderAccessControlAllowOrigin) != "" { | ||
| // CORS headers already present, likely from upstream - skip processing | ||
| if preflight := req.Method == http.MethodOptions; preflight { | ||
| return c.NoContent(http.StatusNoContent) | ||
| } | ||
| return next(c) | ||
| } | ||
|
|
||
| // Add Origin to Vary header only if not already present | ||
| varyHeader := res.Header().Get(echo.HeaderVary) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MEDIUM] Vary header deduplication uses substring match instead of token comparison The code checks if 'Origin' is present in the Vary header using substring matching (strings.Contains). This can incorrectly match headers like 'X-Origin-Custom' or 'Original', causing the code to skip adding 'Origin' when it should be added, leading to incorrect caching behavior. Justification: The substring check on line 208 ( Validation: Line 208: Suggestion: Split the Vary header on ', ' and check for exact token matches. Use a helper function like |
||
| if !strings.Contains(varyHeader, echo.HeaderOrigin) { | ||
| if varyHeader == "" { | ||
| res.Header().Set(echo.HeaderVary, echo.HeaderOrigin) | ||
| } else { | ||
| res.Header().Set(echo.HeaderVary, varyHeader+", "+echo.HeaderOrigin) | ||
| } | ||
| } | ||
|
|
||
| // Preflight request is an OPTIONS request, using three HTTP request headers: Access-Control-Request-Method, | ||
| // Access-Control-Request-Headers, and the Origin header. See: https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request | ||
|
|
@@ -261,8 +279,20 @@ func (config CORSConfig) ToMiddleware() (echo.MiddlewareFunc, error) { | |
| // Preflight will end with c.NoContent(http.StatusNoContent) as we do not know if | ||
| // at the end of handler chain is actual OPTIONS route or 404/405 route which | ||
| // response code will confuse browsers | ||
| res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestMethod) | ||
| res.Header().Add(echo.HeaderVary, echo.HeaderAccessControlRequestHeaders) | ||
|
|
||
| // Add to Vary header only if not already present | ||
| varyHeader = res.Header().Get(echo.HeaderVary) | ||
| varyValues := []string{echo.HeaderAccessControlRequestMethod, echo.HeaderAccessControlRequestHeaders} | ||
| for _, varyValue := range varyValues { | ||
| if !strings.Contains(varyHeader, varyValue) { | ||
| if varyHeader == "" { | ||
| varyHeader = varyValue | ||
| } else { | ||
| varyHeader = varyHeader + ", " + varyValue | ||
| } | ||
| } | ||
| } | ||
| res.Header().Set(echo.HeaderVary, varyHeader) | ||
|
|
||
| if !hasCustomAllowMethods && routerAllowMethods != "" { | ||
| res.Header().Set(echo.HeaderAccessControlAllowMethods, routerAllowMethods) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[HIGH] CORS middleware bypass via pre-existing Access-Control-Allow-Origin header
The middleware checks if the
Access-Control-Allow-Originheader is already set in the response and skips all CORS processing if it is. An attacker or a misconfigured upstream proxy can inject this header, causing the middleware to skip origin validation, method checks, and credential settings, effectively bypassing CORS security.Justification: The code at lines 196-204 introduces a new check: if
res.Header().Get(echo.HeaderAccessControlAllowOrigin) != "", it skips processing and either returns a 204 for preflight or callsnext(c). This allows any upstream component (or an attacker via header injection in a request that proxies through) to set the CORS header and bypass the security validation.Validation: Lines 198-204:
if res.Header().Get(echo.HeaderAccessControlAllowOrigin) != "" { ... // CORS headers already present, likely from upstream - skip processing ... }. This conditional skip occurs before any origin validation (allowOriginFuncis never called), allowing the request to proceed without CORS checks.Suggestion: Remove the early exit based on pre-existing CORS headers. Always validate the origin and apply CORS logic regardless of existing headers, or at least verify the existing header matches the validated origin before skipping.