XSS stands for cross-site scripting; the X is used instead of a C to avoid confusion with CSS style sheets. It is an injection flaw: the site inserts data from the outside into its page without neutralizing it, and the browser interprets that data as code. MITRE catalogs it as CWE-79. Because the script appears to come from the site itself, the browser gives it the same trust as the rest of the page.
How an XSS attack works
It all starts with a value the attacker controls: a URL parameter, a form field, a comment, an HTTP header saved in visitor statistics. If the code outputs that value raw, a string like <script>…</script> or an onerror="…" attribute becomes executable. The script can then read the page content, send requests on behalf of the logged-in user, change what they see or redirect them to another site.

On a WordPress site, the prime target is the administrator. A script running in their session can create an account, install a plugin or change a setting without ever knowing their password: the browser itself sends the login cookies with every request.
The different types of XSS
- Stored (persistent) XSS: the script is saved on the server, in the database for example, then served again every time the data is displayed. The victim doesn't have to do anything special, just open the page.
- Reflected XSS: the script travels in the request, often a booby-trapped link, and the server immediately copies it into its response. The victim has to click.
- DOM-based XSS: the page's own JavaScript inserts untrusted data, such as the fragment of a URL. The server may never see it go by.
OWASP also describes “blind” XSS: a stored variant that fires later, in an interface the attacker cannot see. That is exactly the case for the logs, statistics and form submissions you review from the Dashboard. The real-world XSS cases in popular plugins show this scenario is anything but theoretical.
For WordPress: escape late
The WordPress developer documentation sets a simple rule: escape as late as possible, at the moment the data is output, with the function that matches the context.
| Output context | Function |
|---|---|
| Text inside an HTML element | esc_html() |
| Attribute value | esc_attr() |
| URL in href or src | esc_url() |
| Inline JavaScript | esc_js() |
| Textarea content | esc_textarea() |
| HTML where some tags are allowed | wp_kses(), wp_kses_post() |
<h2><?php echo esc_html( $title ); ?></h2>
<a href="<?php echo esc_url( $link ); ?>" title="<?php echo esc_attr( $info ); ?>">View</a>
Validating and sanitizing input is still useful, but it does not replace output escaping: the same value can be displayed in several contexts.
How to protect your site without touching code
- Update plugins and themes quickly: XSS flaws exploited at scale usually target versions that have already been patched.
- Keep few privileged accounts and delete the ones nobody uses anymore.
- Watch for new administrator accounts, a common sign of an exploited XSS.
- Add a Content Security Policy, keeping in mind that a strict version, which bans inline scripts, is hard to maintain with WordPress. Our guide to HTTP security headers for WordPress covers the options.
- A web application firewall can stop known payloads, with no guarantee against a new variant.
WP Masquerade Pro sends security headers, including an optional basic CSP. That CSP does not restrict scripts, so it does not block an XSS. WP Masquerade is neither a web application firewall nor a code fixer: only updating the faulty component closes the hole.
Common mistakes
- Confusing XSS with CSRF: the latter makes the browser send a forged request without running any script in the page. An XSS, on the other hand, can bypass anti-CSRF protections, since the script reads the tokens in the page.
- Assuming data is “internal” because it shows up in the admin area: an HTTP header or a visited URL comes from the outside.
- Using
esc_html()in an attribute oresc_attr()for a URL: each context has its own function. - Thinking that protection against SQL injection is enough: data stored safely in the database can still be dangerous when displayed.