29
29
// AllowMethods defines a list methods allowed when accessing the resource.
30
30
// This is used in response to a preflight request.
31
31
// 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.
32
34
AllowMethods []string `yaml:"allow_methods"`
33
35
34
36
// AllowHeaders defines a list of request headers that can be used when
41
43
// a response to a preflight request, this indicates whether or not the
42
44
// actual request can be made using credentials.
43
45
// 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
44
48
AllowCredentials bool `yaml:"allow_credentials"`
45
49
46
50
// ExposeHeaders defines a whitelist headers that clients are allowed to
@@ -80,7 +84,9 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
80
84
if len (config .AllowOrigins ) == 0 {
81
85
config .AllowOrigins = DefaultCORSConfig .AllowOrigins
82
86
}
87
+ hasCustomAllowMethods := true
83
88
if len (config .AllowMethods ) == 0 {
89
+ hasCustomAllowMethods = false
84
90
config .AllowMethods = DefaultCORSConfig .AllowMethods
85
91
}
86
92
@@ -109,10 +115,28 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
109
115
origin := req .Header .Get (echo .HeaderOrigin )
110
116
allowOrigin := ""
111
117
112
- preflight := req .Method == http .MethodOptions
113
118
res .Header ().Add (echo .HeaderVary , echo .HeaderOrigin )
114
119
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
116
140
if origin == "" {
117
141
if ! preflight {
118
142
return next (c )
@@ -145,19 +169,15 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
145
169
}
146
170
}
147
171
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 {
161
181
if match , _ := regexp .MatchString (re , origin ); match {
162
182
allowOrigin = origin
163
183
break
@@ -174,12 +194,13 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
174
194
return c .NoContent (http .StatusNoContent )
175
195
}
176
196
197
+ res .Header ().Set (echo .HeaderAccessControlAllowOrigin , allowOrigin )
198
+ if config .AllowCredentials {
199
+ res .Header ().Set (echo .HeaderAccessControlAllowCredentials , "true" )
200
+ }
201
+
177
202
// Simple request
178
203
if ! preflight {
179
- res .Header ().Set (echo .HeaderAccessControlAllowOrigin , allowOrigin )
180
- if config .AllowCredentials {
181
- res .Header ().Set (echo .HeaderAccessControlAllowCredentials , "true" )
182
- }
183
204
if exposeHeaders != "" {
184
205
res .Header ().Set (echo .HeaderAccessControlExposeHeaders , exposeHeaders )
185
206
}
@@ -189,11 +210,13 @@ func CORSWithConfig(config CORSConfig) echo.MiddlewareFunc {
189
210
// Preflight request
190
211
res .Header ().Add (echo .HeaderVary , echo .HeaderAccessControlRequestMethod )
191
212
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 )
196
218
}
219
+
197
220
if allowHeaders != "" {
198
221
res .Header ().Set (echo .HeaderAccessControlAllowHeaders , allowHeaders )
199
222
} else {
0 commit comments