Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
••
Sichere ProgrammierungCloudflare outage 2019: how one regex caused 27 minutes of 502s(29.09.2026 um 05:46 Uhr)
•
Sichere ProgrammierungHow boxr runs rootless: namespaces, a trampoline, and uid mapping(29.09.2026 um 05:47 Uhr)
••
Sichere ProgrammierungTech Stack for Thumbrella Cloud(29.09.2026 um 05:51 Uhr)
•
AI & KI NachrichtenStructured Outputs vs function calling in the OpenAI API(29.09.2026 um 05:53 Uhr)
•
Sichere ProgrammierungSMS OTP API Ownership: 6 Rate Limit Rules for SaaS Login Recovery(29.09.2026 um 05:55 Uhr)
•
Sichere ProgrammierungHow I Review AI-Generated HTML Before Publishing It(29.09.2026 um 05:58 Uhr)
•
Sichere ProgrammierungAI - Weights(29.09.2026 um 05:59 Uhr)
•••
Sichere ProgrammierungCloudflare outage 2019: how one regex caused 27 minutes of 502s(29.09.2026 um 05:46 Uhr)
•
Sichere ProgrammierungHow boxr runs rootless: namespaces, a trampoline, and uid mapping(29.09.2026 um 05:47 Uhr)
••
Sichere ProgrammierungTech Stack for Thumbrella Cloud(29.09.2026 um 05:51 Uhr)
•
AI & KI NachrichtenStructured Outputs vs function calling in the OpenAI API(29.09.2026 um 05:53 Uhr)
•
Sichere ProgrammierungSMS OTP API Ownership: 6 Rate Limit Rules for SaaS Login Recovery(29.09.2026 um 05:55 Uhr)
•
Sichere ProgrammierungHow I Review AI-Generated HTML Before Publishing It(29.09.2026 um 05:58 Uhr)
•
Sichere ProgrammierungAI - Weights(29.09.2026 um 05:59 Uhr)
•
Intelligence View
⚡ tsecurity.de Intelligence

CSRF: Why Double-Submit Cookie Falls Short for Financial-Grade Security

In the previous articles, we hardened the login: constant timing, progressive lockout, no existence leaks. The user is authenticated. Now the question is: how do you protect the actions they take once logged in? CSRF. The topic everyone…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

In the previous articles, we hardened the login: constant timing, progressive lockout, no existence leaks. The user is authenticated. Now the question is: how do you protect the actions they take once logged in?



CSRF. The topic everyone thinks they've mastered because they put a token in a hidden field in 2015. Except the double-submit cookie — the pattern found in 80% of implementations — has weaknesses most developers ignore.





Double-submit cookie works like this: the server sets a cookie csrf_token=abc123, the form sends the same token in a hidden field, the server compares both. If an attacker can't read the domain's cookies, they can't forge the request.



The problem: the attacker doesn't need to read the cookie. They need to set it. And in several scenarios, that's possible:




  • Compromised subdomain — an XSS on blog.example.com can set a cookie for .example.com

  • HTTP downgrade — if a single page on the domain is accessible via HTTP (even a redirect), a MITM attacker can inject a Set-Cookie

  • Cookie tossing — some browsers accept cookies set by parent domains



If the attacker can set the CSRF cookie, they control both values (cookie and form field) and double-submit protects nothing.



For an authentication service handling sensitive operations, that's not acceptable. OWASP ASVS V4.2.2 explicitly recommends the synchronizer token pattern for high-risk contexts.






Synchronizer token: server-side storage



The synchronizer token is straightforward: the CSRF token is generated server-side, stored in the session (DB or Redis), and compared in constant-time on every mutative request.




// Token generation at session creation
func generateCSRFToken() string {
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
panic(err) // crypto/rand failure = immediate stop
}
return base64.RawURLEncoding.EncodeToString(b)
}

// Store in session
session.CSRFToken = generateCSRFToken()
store.Save(session)






Comparison uses subtle.ConstantTimeCompare — not == — to avoid a timing oracle on the token:




func validateCSRF(session *Session, provided string) bool {
expected := []byte(session.CSRFToken)
got := []byte(provided)
return subtle.ConstantTimeCompare(expected, got) == 1
}






The advantage: the token is never in a cookie. The attacker can neither read it nor set it. The only way to obtain it is DOM access (and at that point, it's XSS, not CSRF).






Middleware wire-order



The order in which you chain your middlewares isn't cosmetic. For CSRF, the middleware must execute:




  1. After session-load — otherwise you don't have the token to compare

  2. Before refresh-user — otherwise a forged CSRF request could trigger a refresh with side-effects




// Correct order
mux.Use(sessionMiddleware) // 1. load session
mux.Use(csrfMiddleware) // 2. validate CSRF token
mux.Use(refreshUserMiddleware) // 3. refresh user data
mux.Use(handler) // 4. process request






I saw a codebase where CSRF middleware came after refresh-user. Result: a forged CSRF request triggered a SELECT + UPDATE last_seen on the user before being rejected. Not a vulnerability per se, but an unnecessary side-effect and a signal that wire-order wasn't thought through.






X-CSRF-Token header for JS requests



HTML forms send the token in a hidden field. But fetch() or XMLHttpRequest calls don't go through a form. For those, the standard pattern: custom X-CSRF-Token header.




// Client side: read token from a meta tag
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;

fetch('/api/action', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken,
},
body: JSON.stringify(data),
});









// Server side: accept token from header OR form field
func extractCSRFToken(r *http.Request) string {
// Header takes priority (JS requests)
if token := r.Header.Get("X-CSRF-Token"); token != "" {
return token
}
// Fallback to form field (HTML forms)
return r.FormValue("csrf_token")
}






The custom header has a bonus: cross-origin requests can't add custom headers without a CORS preflight. That's an extra protection layer, though it shouldn't be the only one.






Token rotation



Two schools: one token per session, or one per request. My take: one per session is sufficient for the vast majority of cases. Per-request tokens add complexity (race conditions on parallel requests, back button invalidation) for marginal security gain.



The only reason to rotate the token: after a privilege change (login, role escalation). And at that point, you're regenerating the entire session anyway.






Conclusion



Double-submit cookie is a fine default for standard applications. But when you handle authentication, financial operations, or high-risk actions, the server-side synchronizer token is the right choice. The token is never in a cookie, comparison is constant-time, and middleware wire-order enforces discipline on the processing chain.



So far we've covered login, brute force, and CSRF. All on a service running mTLS. As it happens, mTLS has its own subtleties: how do you serve three different audiences on a single TLS port with distinct client auth levels? That's the subject of the next article.

2. Cyber Threat Intelligence & Forensik

CTI Threat Relationship Graph3 Knoten / 2 Relationen
CVE / Incident Software MITRE ATT&CK CWE Weakness IoC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten CSRF: Why Double-Submit Cookie Falls Short for Financial-Grade Security

Thematisch verwandte Begriffe: CSRF, DoubleSubmit, Cookie, Falls · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-101093 | Cotonti through 1.0.0 contains a cross-site request forgery vulnerabili…
Advisory →
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag