Insecure or unset HTTP headers - CORS
Need
Restriction of cross-origin access to explicitly trusted origins
Context
• Usage of Rust 1.75+ for building application services
• Usage of the actix-web crate for handling HTTP requests
• Usage of the actix-cors crate for cross-origin resource sharing
Description
1. Non compliant code
use actix_cors::Cors;
use actix_web::{get, App, HttpResponse, HttpServer};
fn cors() -> Cors {
// Any website can make requests with the user's cookies and read the response
Cors::permissive()
}
...The `cors` function below configures the application with `Cors::permissive()` from the `actix-cors` crate. `Cors::permissive()` accepts any origin, any method and any header, reflects the origin of each request in `Access-Control-Allow-Origin`, and allows credentials. As a result, a script running on any website can send authenticated requests to the API with the cookies of the visitor, and read the responses. An attacker who lures a logged-in user to a malicious page can read their profile, tokens or business data, or perform actions on their behalf. `Cors::default().allow_any_origin()` and a manual `Access-Control-Allow-Origin: *` header, set with `insert_header` or `DefaultHeaders`, open the API to every origin in the same way.
2. Steps
• Replace `Cors::permissive()` and `allow_any_origin()` with `Cors::default()` plus one `allowed_origin` call per trusted origin.
• Remove manual `Access-Control-Allow-Origin` headers set to `*` with `insert_header` or `DefaultHeaders`.
• List only the methods and request headers the clients need with `allowed_methods` and `allowed_headers`.
• Enable `supports_credentials` only together with explicit origins, never with a wildcard or a reflected origin.
• Validate dynamic origins with `allowed_origin_fn` against an exact allowlist instead of prefix, suffix or substring checks.
3. Secure code example
use actix_cors::Cors;
use actix_web::http::header;
use actix_web::{get, App, HttpResponse, HttpServer};
fn cors() -> Cors {
// Only the front end origin may call the API with credentials
Cors::default()
.allowed_origin("https://app.example.com")...The corrected `cors` function starts from `Cors::default()`, which denies every cross-origin request, and allows only the origin of the application front end with `allowed_origin`. It also lists the exact methods and request headers the front end uses, and caches the preflight answer for one hour with `max_age`. Requests from any other origin receive no CORS headers, so the browser blocks the calling script from reading the response. `supports_credentials` is enabled only because the origin is explicit. Credentials must never be combined with a wildcard or a reflected origin, and when a list of origins is needed, it should be a fixed allowlist rather than a pattern such as a suffix check that `evil-example.com` could also satisfy.
References
• 134. Insecure or unset HTTP headers - CORS