Insecure or unset HTTP headers - Content-Security-Policy
Need
Enforcement of a restrictive Content-Security-Policy without unsafe directives
Context
• Usage of Rust 1.75+ for building application services
• Usage of the actix-web crate for handling HTTP requests
Description
1. Non compliant code
use actix_web::middleware::DefaultHeaders;
use actix_web::{get, App, HttpResponse, HttpServer};
fn security_headers() -> DefaultHeaders {
// 'unsafe-inline' and 'unsafe-eval' let injected scripts run
DefaultHeaders::new().add((
"Content-Security-Policy",
"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'",...The `security_headers` function below configures the actix-web `DefaultHeaders` middleware to send a Content-Security-Policy on every response. The policy allows scripts from `'self'`, but it also includes `'unsafe-inline'` and `'unsafe-eval'`. A Content-Security-Policy is the browser-side defense that stops injected scripts from running when an output encoding mistake lets them into the page. `'unsafe-inline'` allows every `<script>` block and every event handler attribute such as `onerror=` in the document, which are exactly what a cross-site scripting payload uses. `'unsafe-eval'` allows `eval`, `Function` and string arguments to `setTimeout`, which turns any injected string into code. With these directives the header is present but ineffective: a scanner sees a CSP, and an attacker who finds an XSS runs their script as if there were none. Wildcard sources such as `default-src *` or `script-src https:` weaken the policy in the same way.
2. Steps
• Remove `'unsafe-inline'` and `'unsafe-eval'` from every Content-Security-Policy set with `DefaultHeaders` or `insert_header`.
• Replace wildcard sources such as `*`, `https:` or `data:` in `default-src` and `script-src` with `'self'` and the specific hosts the application needs.
• Move inline scripts and event handler attributes to separate script files, or allow the few that remain with a per-response nonce or a hash.
• Add `object-src 'none'`, `base-uri 'self'` and `frame-ancestors 'none'` to the policy.
• Send the policy from a single middleware so every response, including error pages, receives it.
• Test changes with the `Content-Security-Policy-Report-Only` header and a `report-to` endpoint before enforcing them.
3. Secure code example
use actix_web::middleware::DefaultHeaders;
use actix_web::{get, App, HttpResponse, HttpServer};
const CSP: &str = "default-src 'self'; script-src 'self'; object-src 'none'; \
base-uri 'self'; form-action 'self'; frame-ancestors 'none'";
fn security_headers() -> DefaultHeaders {
// Only same-origin script files run; inline code and eval are blocked...The corrected policy removes `'unsafe-inline'` and `'unsafe-eval'` and restricts every resource type to the origin of the application. `default-src 'self'` and `script-src 'self'` only allow scripts loaded from files on the same origin, so inline blocks and event handler attributes injected into the page do not execute. `object-src 'none'` disables plugins, `base-uri 'self'` prevents an injected `<base>` tag from redirecting relative script URLs, `form-action 'self'` stops forms from posting data to other sites, and `frame-ancestors 'none'` prevents the page from being framed. Inline scripts must move to separate files. When a page really needs an inline block, the policy can allow it with a per-response random nonce (`'nonce-...'`) or a hash of its content, but never with `'unsafe-inline'`.