Skip to content

Commit f0cd3b4

Browse files
gabyReneWerner87
andauthored
Merge pull request from GHSA-fmg4-x8pw-hjhg
* Enforce Wildcard Origins with AllowCredentials check * Expand unit-tests, fix issues with subdomains logic, update docs * Update cors.md * Added test using localhost, ipv4, and ipv6 address * improve documentation markdown --------- Co-authored-by: René Werner <[email protected]>
1 parent 5e30112 commit f0cd3b4

File tree

5 files changed

+402
-88
lines changed

5 files changed

+402
-88
lines changed

docs/api/middleware/cors.md

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ The middleware conforms to the `access-control-allow-origin` specification by pa
1010

1111
For more control, `AllowOriginsFunc` can be used to programatically determine if an origin is allowed. If no match was found in `AllowOrigins` and if `AllowOriginsFunc` returns true then the 'access-control-allow-origin' response header is set to the 'origin' request header.
1212

13+
When defining your Origins make sure they are properly formatted. The middleware validates and normalizes the provided origins, ensuring they're in the correct format by checking for valid schemes (http or https), and removing any trailing slashes.
14+
1315
## Signatures
1416

1517
```go
@@ -56,18 +58,27 @@ app.Use(cors.New(cors.Config{
5658
}))
5759
```
5860

61+
**Note: The following configuration is considered insecure and will result in a panic.**
62+
63+
```go
64+
app.Use(cors.New(cors.Config{
65+
AllowOrigins: "*",
66+
AllowCredentials: true,
67+
}))
68+
```
69+
5970
## Config
6071

61-
| Property | Type | Description | Default |
62-
|:-----------------|:---------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------|
63-
| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` |
64-
| AllowOriginsFunc | `func(origin string) bool` | AllowOriginsFunc defines a function that will set the 'access-control-allow-origin' response header to the 'origin' request header when returned true. | `nil` |
65-
| AllowOrigins | `string` | AllowOrigin defines a comma separated list of origins that may access the resource. | `"*"` |
66-
| AllowMethods | `string` | AllowMethods defines a list of methods allowed when accessing the resource. This is used in response to a preflight request. | `"GET,POST,HEAD,PUT,DELETE,PATCH"` |
67-
| AllowHeaders | `string` | AllowHeaders defines a list of request headers that can be used when making the actual request. This is in response to a preflight request. | `""` |
68-
| AllowCredentials | `bool` | AllowCredentials indicates whether or not the response to the request can be exposed when the credentials flag is true. | `false` |
69-
| ExposeHeaders | `string` | ExposeHeaders defines a whitelist headers that clients are allowed to access. | `""` |
70-
| MaxAge | `int` | MaxAge indicates how long (in seconds) the results of a preflight request can be cached. If you pass MaxAge 0, Access-Control-Max-Age header will not be added and browser will use 5 seconds by default. To disable caching completely, pass MaxAge value negative. It will set the Access-Control-Max-Age header 0. | `0` |
72+
| Property | Type | Description | Default |
73+
|:-----------------|:---------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------|
74+
| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` |
75+
| AllowOriginsFunc | `func(origin string) bool` | AllowOriginsFunc defines a function that will set the 'access-control-allow-origin' response header to the 'origin' request header when returned true. This allows for dynamic evaluation of allowed origins. Note if AllowCredentials is true, wildcard origins will be not have the 'access-control-allow-credentials' header set to 'true'. | `nil` |
76+
| AllowOrigins | `string` | AllowOrigin defines a comma separated list of origins that may access the resource. | `"*"` |
77+
| AllowMethods | `string` | AllowMethods defines a list of methods allowed when accessing the resource. This is used in response to a preflight request. | `"GET,POST,HEAD,PUT,DELETE,PATCH"` |
78+
| AllowHeaders | `string` | AllowHeaders defines a list of request headers that can be used when making the actual request. This is in response to a preflight request. | `""` |
79+
| AllowCredentials | `bool` | AllowCredentials indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials. Note: If true, AllowOrigins cannot be set to a wildcard ("*") to prevent security vulnerabilities. | `false` |
80+
| ExposeHeaders | `string` | ExposeHeaders defines a whitelist headers that clients are allowed to access. | `""` |
81+
| MaxAge | `int` | MaxAge indicates how long (in seconds) the results of a preflight request can be cached. If you pass MaxAge 0, Access-Control-Max-Age header will not be added and browser will use 5 seconds by default. To disable caching completely, pass MaxAge value negative. It will set the Access-Control-Max-Age header 0. | `0` |
7182

7283
## Default Config
7384

middleware/cors/cors.go

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ type Config struct {
1616
Next func(c *fiber.Ctx) bool
1717

1818
// AllowOriginsFunc defines a function that will set the 'access-control-allow-origin'
19-
// response header to the 'origin' request header when returned true.
19+
// response header to the 'origin' request header when returned true. This allows for
20+
// dynamic evaluation of allowed origins. Note if AllowCredentials is true, wildcard origins
21+
// will be not have the 'access-control-allow-credentials' header set to 'true'.
2022
//
2123
// Optional. Default: nil
2224
AllowOriginsFunc func(origin string) bool
2325

24-
// AllowOrigin defines a list of origins that may access the resource.
26+
// AllowOrigin defines a comma separated list of origins that may access the resource.
2527
//
2628
// Optional. Default value "*"
2729
AllowOrigins string
@@ -41,7 +43,8 @@ type Config struct {
4143
// AllowCredentials indicates whether or not the response to the request
4244
// can be exposed when the credentials flag is true. When used as part of
4345
// a response to a preflight request, this indicates whether or not the
44-
// actual request can be made using credentials.
46+
// actual request can be made using credentials. Note: If true, AllowOrigins
47+
// cannot be set to a wildcard ("*") to prevent security vulnerabilities.
4548
//
4649
// Optional. Default value false.
4750
AllowCredentials bool
@@ -105,6 +108,26 @@ func New(config ...Config) fiber.Handler {
105108
log.Warn("[CORS] Both 'AllowOrigins' and 'AllowOriginsFunc' have been defined.")
106109
}
107110

111+
// Validate CORS credentials configuration
112+
if cfg.AllowCredentials && cfg.AllowOrigins == "*" {
113+
panic("[CORS] Insecure setup, 'AllowCredentials' is set to true, and 'AllowOrigins' is set to a wildcard.")
114+
}
115+
116+
// Validate and normalize static AllowOrigins if not using AllowOriginsFunc
117+
if cfg.AllowOriginsFunc == nil && cfg.AllowOrigins != "" && cfg.AllowOrigins != "*" {
118+
validatedOrigins := []string{}
119+
for _, origin := range strings.Split(cfg.AllowOrigins, ",") {
120+
isValid, normalizedOrigin := normalizeOrigin(origin)
121+
if isValid {
122+
validatedOrigins = append(validatedOrigins, normalizedOrigin)
123+
} else {
124+
log.Warnf("[CORS] Invalid origin format in configuration: %s", origin)
125+
panic("[CORS] Invalid origin provided in configuration")
126+
}
127+
}
128+
cfg.AllowOrigins = strings.Join(validatedOrigins, ",")
129+
}
130+
108131
// Convert string to slice
109132
allowOrigins := strings.Split(strings.ReplaceAll(cfg.AllowOrigins, " ", ""), ",")
110133

@@ -123,22 +146,18 @@ func New(config ...Config) fiber.Handler {
123146
return c.Next()
124147
}
125148

126-
// Get origin header
127-
origin := c.Get(fiber.HeaderOrigin)
149+
// Get originHeader header
150+
originHeader := c.Get(fiber.HeaderOrigin)
128151
allowOrigin := ""
129152

130153
// Check allowed origins
131-
for _, o := range allowOrigins {
132-
if o == "*" {
154+
for _, origin := range allowOrigins {
155+
if origin == "*" {
133156
allowOrigin = "*"
134157
break
135158
}
136-
if o == origin {
137-
allowOrigin = o
138-
break
139-
}
140-
if matchSubdomain(origin, o) {
141-
allowOrigin = origin
159+
if validateDomain(originHeader, origin) {
160+
allowOrigin = originHeader
142161
break
143162
}
144163
}
@@ -147,8 +166,8 @@ func New(config ...Config) fiber.Handler {
147166
// handling the value in 'AllowOrigins' does
148167
// not result in allowOrigin being set.
149168
if allowOrigin == "" && cfg.AllowOriginsFunc != nil {
150-
if cfg.AllowOriginsFunc(origin) {
151-
allowOrigin = origin
169+
if cfg.AllowOriginsFunc(originHeader) {
170+
allowOrigin = originHeader
152171
}
153172
}
154173

@@ -173,9 +192,17 @@ func New(config ...Config) fiber.Handler {
173192
c.Set(fiber.HeaderAccessControlAllowOrigin, allowOrigin)
174193
c.Set(fiber.HeaderAccessControlAllowMethods, allowMethods)
175194

176-
// Set Allow-Credentials if set to true
177195
if cfg.AllowCredentials {
178-
c.Set(fiber.HeaderAccessControlAllowCredentials, "true")
196+
// When AllowCredentials is true, set the Access-Control-Allow-Origin to the specific origin instead of '*'
197+
if allowOrigin != "*" && allowOrigin != "" {
198+
c.Set(fiber.HeaderAccessControlAllowOrigin, allowOrigin)
199+
c.Set(fiber.HeaderAccessControlAllowCredentials, "true")
200+
} else if allowOrigin == "*" {
201+
log.Warn("[CORS] 'AllowCredentials' is true, but 'AllowOrigins' cannot be set to '*'.")
202+
}
203+
} else {
204+
// For non-credential requests, it's safe to set to '*' or specific origins
205+
c.Set(fiber.HeaderAccessControlAllowOrigin, allowOrigin)
179206
}
180207

181208
// Set Allow-Headers if not empty

0 commit comments

Comments
 (0)