-
Notifications
You must be signed in to change notification settings - Fork 0
CORS is not CSRF
Worth its own page, because the belief that it is causes real vulnerabilities.
A simple cross-origin request — a form POST with
application/x-www-form-urlencoded, multipart/form-data or text/plain —
gets no preflight. The browser sends it. The server receives it. The server
commits the state change.
CORS then stops the attacker from reading the response — which does nothing about the transfer that already happened.
<!-- On attacker.example. No preflight; the browser just sends it. -->
<form action="https://bank.example/transfer" method="POST">
<input name="to" value="attacker">
<input name="amount" value="10000">
</form>
<script>document.forms[0].submit()</script>The victim's cookies ride along. A CORS policy that allows no origin at all does
not stop this request being made — it only stops the attacker reading the
{"status":"ok"} that comes back.
| Question | Answered by | |
|---|---|---|
| CORS | may this origin read my responses? | rextension-cors |
| CSRF | did this request really come from my own application? | rextension-security |
They share an origin allowlist — rextension.OriginPolicy, declared in the
contract module — and nothing else.
origins := rextension.OriginPolicy{
AllowedOrigins: []string{"https://app.example.com"},
AllowCredentials: true,
}
app := rex.New(
cors.WithCORS(cors.NewConfig(cors.WithPolicy(origins))),
security.WithSecurity(&security.Config{
CSRF: security.CSRFConfig{Policy: origins},
}),
)If the application authenticates with cookies, you need it. The browser attaches cookies to cross-site requests automatically, which is the entire attack.
If it authenticates with an Authorization header — a bearer token held in
memory or in localStorage and set by script — CSRF is largely moot, because an
attacker's page cannot make the browser attach that header. But note that this
holds only while nothing in the application accepts a cookie as a fallback.
CSRF protection applies only to unsafe methods. rextension.SafeMethod
reports which those are, in the RFC 9110 sense. That is not a convenience: a GET
that changes state is a bug in its own right, and one CSRF protection cannot
fix, because a browser will issue it from an <img> tag with no way for the
server to distinguish it.
Referer is not a fallback for Origin. It is stripped by privacy
settings, by referrer policies and by some proxies — so treating its absence as
"no cross-origin request" would be a bypass, and treating its presence as
authoritative would let a referrer policy weaken the check.
rextension.RequestOrigin reads Origin and only Origin.
rextension-cors — Cross-Origin Resource Sharing for Rex · MIT · © 2026 Kryovyx
Ecosystem