What is XSS and how can you prevent it?

Author: Published

In brief: XSS (Cross-Site Scripting) is a vulnerability that causes a browser to run untrusted code in the context of the website being visited. It most often arises when an application treats user input as part of a page or as code rather than plain text. Protection is primarily about displaying data safely, not just blocking suspicious input.

How XSS works and what an attacker can do

The browser interprets HTML as the page structure and JavaScript as instructions to execute. If an application inserts unvalidated content into a location interpreted as code, an attacker can affect how the page behaves for another user. That content might come from a comment, a URL parameter, a profile name or a response from an external service.

Code executed through XSS runs in the context of the affected website. It can read accessible data on the page, change its content, display a fake login form or perform operations with the logged-in user’s permissions. The actual impact depends on the application’s features and the safeguards in place.

This does not automatically mean that the server or the entire computer has been compromised. The problem primarily concerns the browser’s trust in code run by a particular website. A user can open a legitimate address and still encounter a page containing malicious content.

The three most commonly described forms of XSS

These categories help identify where unsafe content comes from and where the application processes it:

These terms are not always mutually exclusive. “Stored” and “reflected” mainly describe how data is delivered, while “DOM-based” identifies where the unsafe processing happens. Content stored in a database can also lead to DOM-based XSS.

A simple example: text treated as HTML

Suppose an application displays a user’s name. Run the following example only in your own test environment or somewhere you have explicit permission:

const name = '<img src="x" onerror="alert(\'XSS test\')">';
const element = document.querySelector('#greeting');
element.innerHTML = name;

The innerHTML property tells the browser to interpret the value as HTML. If the image fails to load, the error handler may run alert, displaying a message box. This demonstrates code execution, not data theft; additional browser or website safeguards may block it.

If the name is meant to be plain text, the appropriate solution is:

const name = '<img src="x" onerror="alert(\'XSS test\')">';
const element = document.querySelector('#greeting');
element.textContent = name;

The textContent property displays the supplied string without creating an image or executing the instruction it contains. The distinction is fundamental: data remains data. There is no need to guess which words or tags might be dangerous.

How to protect an application against XSS

Match the protection to where the data is used. Output encoding represents special characters in a way that prevents them from being interpreted as syntax. Text in HTML, an attribute value, a URL and a JavaScript snippet each require different rules. Replacing only the angle brackets < and > does not protect all these contexts.

When displaying plain text, use safe templating mechanisms and interfaces such as textContent. A framework’s automatic encoding helps, but a function that inserts raw HTML can bypass it. Such exceptions need separate review.

If users genuinely need HTML formatting, use an actively maintained sanitization library such as DOMPurify. Sanitization removes disallowed elements and attributes according to defined rules. A custom filter based on a few regular expressions does not provide equivalent protection.

Validation—checking that data has an allowed format—remains useful: an age field should accept a number, and a link address should use an allowed scheme. However, validation does not replace safe display of data. Protecting only the form is not enough either, because data can enter the application through other routes.

Additional layers limit the impact of mistakes. CSP (Content Security Policy) lets you define rules for script execution, but its effectiveness depends on its configuration. The HttpOnly attribute prevents JavaScript from reading a cookie, but it does not prevent malicious code from performing every possible action within an active session.

Where to start checking your own website

Trace one field from the point where its data is received to every place where that data is displayed. A comment may be safe beneath an article but unsafe in the moderation panel or a notification preview. Also review uses of innerHTML and functions that allow automatic encoding to be bypassed.

The next step is a test in an isolated environment: determine whether the entered content appears as text or changes the page structure. After fixing the issue, add a regression test that will detect if this data is ever treated as HTML again.

See also: What is 2FA?

Read also

Learn how PHP-FPM handles PHP requests, how it differs from a web server, and how to tune processes for traffic and hosting resources.
Learn how BIND, open-source DNS server software, handles domains, how its two roles differ and when running it yourself makes sense.
Learn what Poland’s REGON register and identification number mean, how they differ from NIP and KRS, and how to check a business’s details.