2929 // AllowMethods defines a list methods allowed when accessing the resource.
3030 // This is used in response to a preflight request.
3131 // Optional. Default value DefaultCORSConfig.AllowMethods.
32+ // If `allowMethods` is left empty will fill for preflight request `Access-Control-Allow-Methods` header value
33+ // from `Allow` header that echo.Router set into context.
3234 AllowMethods []string `yaml:"allow_methods"`
3335
3436 // AllowHeaders defines a list of request headers that can be used when
4143 // a response to a preflight request, this indicates whether or not the
4244 // actual request can be made using credentials.
4345 // Optional. Default value false.
46+ // Security: avoid using `AllowCredentials = true` with `AllowOrigins = *`.
47+ // See http://blog.portswigger.net/2016/10/exploiting-cors-misconfigurations-for.html
4448 AllowCredentials bool `yaml:"allow_credentials"`
4549
4650 // ExposeHeaders defines a whitelist headers that clients are allowed to
@@ -80,7 +84,9 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
8084 if len (config .AllowOrigins ) == 0 {
8185 config .AllowOrigins = DefaultCORSConfig .AllowOrigins
8286 }
87+ hasCustomAllowMethods := true
8388 if len (config .AllowMethods ) == 0 {
89+ hasCustomAllowMethods = false
8490 config .AllowMethods = DefaultCORSConfig .AllowMethods
8591 }
8692
@@ -109,10 +115,28 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
109115 origin := req .Header .Get (echo .HeaderOrigin )
110116 allowOrigin := ""
111117
112- preflight := req .Method == http .MethodOptions
113118 res .Header ().Add (echo .HeaderVary , echo .HeaderOrigin )
114119
115- // No Origin provided
120+ // Preflight request is an OPTIONS request, using three HTTP request headers: Access-Control-Request-Method,
121+ // Access-Control-Request-Headers, and the Origin header. See: https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
122+ // For simplicity we just consider method type and later `Origin` header.
123+ preflight := req .Method == http .MethodOptions
124+
125+ // Although router adds special handler in case of OPTIONS method we avoid calling next for OPTIONS in this middleware
126+ // as CORS requests do not have cookies / authentication headers by default, so we could get stuck in auth
127+ // middlewares by calling next(c).
128+ // But we still want to send `Allow` header as response in case of Non-CORS OPTIONS request as router default
129+ // handler does.
130+ routerAllowMethods := ""
131+ if preflight {
132+ tmpAllowMethods , ok := c .Get (echo .ContextKeyHeaderAllow ).(string )
133+ if ok && tmpAllowMethods != "" {
134+ routerAllowMethods = tmpAllowMethods
135+ c .Response ().Header ().Set (echo .HeaderAllow , routerAllowMethods )
136+ }
137+ }
138+
139+ // No Origin provided. This is (probably) not request from actual browser - proceed executing middleware chain
116140 if origin == "" {
117141 if ! preflight {
118142 return next (c )
@@ -145,19 +169,15 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
145169 }
146170 }
147171
148- // Check allowed origin patterns
149- for _ , re := range allowOriginPatterns {
150- if allowOrigin == "" {
151- didx := strings .Index (origin , "://" )
152- if didx == - 1 {
153- continue
154- }
155- domAuth := origin [didx + 3 :]
156- // to avoid regex cost by invalid long domain
157- if len (domAuth ) > 253 {
158- break
159- }
160-
172+ checkPatterns := false
173+ if allowOrigin == "" {
174+ // to avoid regex cost by invalid (long) domains (253 is domain name max limit)
175+ if len (origin ) <= (253 + 3 + 4 ) && strings .Contains (origin , "://" ) {
176+ checkPatterns = true
177+ }
178+ }
179+ if checkPatterns {
180+ for _ , re := range allowOriginPatterns {
161181 if match , _ := regexp .MatchString (re , origin ); match {
162182 allowOrigin = origin
163183 break
@@ -174,12 +194,13 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
174194 return c .NoContent (http .StatusNoContent )
175195 }
176196
197+ res .Header ().Set (echo .HeaderAccessControlAllowOrigin , allowOrigin )
198+ if config .AllowCredentials {
199+ res .Header ().Set (echo .HeaderAccessControlAllowCredentials , "true" )
200+ }
201+
177202 // Simple request
178203 if ! preflight {
179- res .Header ().Set (echo .HeaderAccessControlAllowOrigin , allowOrigin )
180- if config .AllowCredentials {
181- res .Header ().Set (echo .HeaderAccessControlAllowCredentials , "true" )
182- }
183204 if exposeHeaders != "" {
184205 res .Header ().Set (echo .HeaderAccessControlExposeHeaders , exposeHeaders )
185206 }
@@ -189,11 +210,13 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
189210 // Preflight request
190211 res .Header ().Add (echo .HeaderVary , echo .HeaderAccessControlRequestMethod )
191212 res .Header ().Add (echo .HeaderVary , echo .HeaderAccessControlRequestHeaders )
192- res .Header ().Set (echo .HeaderAccessControlAllowOrigin , allowOrigin )
193- res .Header ().Set (echo .HeaderAccessControlAllowMethods , allowMethods )
194- if config .AllowCredentials {
195- res .Header ().Set (echo .HeaderAccessControlAllowCredentials , "true" )
213+
214+ if ! hasCustomAllowMethods && routerAllowMethods != "" {
215+ res .Header ().Set (echo .HeaderAccessControlAllowMethods , routerAllowMethods )
216+ } else {
217+ res .Header ().Set (echo .HeaderAccessControlAllowMethods , allowMethods )
196218 }
219+
197220 if allowHeaders != "" {
198221 res .Header ().Set (echo .HeaderAccessControlAllowHeaders , allowHeaders )
199222 } else {
0 commit comments