Report your CSP (violations)
I'm a big fan of a strict Content Security Policy (CSP). It can be a pain to setup (but there is help, here, here, here and here) and evaluate.
Let's report it
You won't know if a policy was tried to be violated unless the attempt is reported back to your application. For that purpose we use the CSP directives report-to
and the older, deprecated report-uri
. Until Firefox catches up, use both. When a browser supports report-to
, report-uri
gets ignored.
Reporting isn't only useful in production, but already during development and especially during retrofitting. There you can swap the Content-Security-Policy
header for Content-Security-Policy-Report-Only
. It will allow all content to load, but report back all violations.
You then run your E2E Tests (You have those, haven't you?) and get a free overview what loads from where. Adjust the CSP, rinse and repeat.
Where to report to?
There are numerous SaaS providers with fancy dashboards, that offer ready made solutions. When you are pressed for time, that might be your best option. I haven't evaluated them, so I can't recommend or endorse them.
What I can do, is showing you server side code snippets, to integrate reporting into your application. A CSP violation report has the mime type application/reports+json
and for the older format application/csp-report
, we shall use this.
Vert.x
The Domino REST API got implemented using Vert.x, so this goes first.
public void cspReport(final Router router) {
router.route("/csp-violation-report")
.method(HttpMethod.POST)
.consumes("application/csp-report",
"application/reports+json")
.handler(ctx -> {
try {
final JsonObject json = ctx.body().asJsonObject();
// Do something with the log
logCspViolation(json);
} catch (Exception e) {
// Add you loggin here
}
ctx.response().setStatusCode(204).end();
});
}
Quarkus
Quarkus REST goes next.
@POST
@Path("/csp-violation-report")
@Consumes("application/csp-report","application/reports+json")
public void cspReport(final HttpServerResponse response, final JsonObject body) {
// Do something with the log
logCspViolation(body);
response.setStatusCode(204).end();
}
ExpressJS
The popular NodeJS web framework ExpressJS next:
const setupCspReportRoute = (app) => {
app.post('/csp-violation-report', express.json(), (req, res) => {
logCspViolation(req.body);
res.status(204).end();
});
};
Rust / Rocket
It isn't done, until it has been rewritten in Rust, in this case using Rocket.
use rocket::{post, routes, serde::json::Json, http::Status};
use serde_json::Value;
use my_app::logCspViolation;
#[post("/csp-violation-report", format = "json", data = "<body>")]
fn csp_report(body: Json<Value>) -> Status {
logCspViolation(&body);
Status::NoContent
}
// To mount the route in your Rocket app:
rocket::build().mount("/", routes![csp_report]);
Rust / Axum
There are more options around in Rust, e.g. Axum
use axum::{routing::post, Json, Router, http::StatusCode};
use serde_json::Value;
use my_app::logCspViolation;
async fn csp_report(Json(body): Json<Value>) -> StatusCode {
logCspViolation(&body);
StatusCode::NO_CONTENT
}
// To mount the route in your Axum app:
let app = Router::new().route("/csp-violation-report", post(csp_report));
How do you handle CSP?
As usual YMMV
Posted by Stephan H Wissel on 07 July 2025 | Comments (0) | categories: Java JavaScript Rust WebDevelopment