⚠️ What is Cross-site scripting (XSS) and How to prevent WordPress XSS attacks

The most regularly seen attack type is script injection (XSS attack), rogue scripts are injected into the webpage for malicious purposes. This includes redirects to third-party websites, collecting user data, downloading malware to visitors, etc.

WordPress has a bunch of useful developer functions that are used to sanitize data (Validating Sanitizing and Escaping User Data) that should be used by every plugin/theme developer.


Cross-site scripting (XSS) attacks occur when code is injected into a website. They are divided into three main categories based on whether the injected code runs on the server or only in the attacker’s browser.

There are three types of XSS attacks:

StoredHackers inject code inside a vulnerable page and then that code is executed every time an unsuspecting victim views the infected page.
ReflectedHackers inject code inside a single HTTP request, meaning that no code is stored in the infected page, but they get response with no security measures in place to prevent it.
DOM basedInjected code runs only inside visitor’s browser, and no code is actually stored on the web-server.

Reflected XSS (AKA Type 1) is the simplest method of cross-site scripting, occurs when WordPress receives data in an HTTP request and includes that data within the immediate response in an unsafe way.

Reflected cross-site scripting explained:

  1. Attacker sends malicious script directly to the user
  2. User opens the link and execute the script when website is open
  3. Depending on the actual code user data might be sent back to the attacker

This type of attack is not necessarily visible on the server side (to the website owner) if the hacker uses evading techniques.

example: Reflected XSS on WordPress

Stored XSS (AKA Type 2) occurs when WordPress receives data from an untrusted source and includes that data with future HTTP responses in an unsafe way.

Stored cross-site scripting explained:

  1. Vulnerable script is stored in the WordPress files or database
  2. Attacker exploits this script to inject malicious code
  3. Users visiting the website after the infection retrieve this code
  4. Depending on the actual code user data might be sent back to the attacker

Because the malicious code is stored directly in the website, the attack is visible on the server side (to the WordPress website owner).

example: Stored XSS on WordPress

Document Object Module (DOM) based XSS (AKA Type 0) occurs when WordPress page contains some client-side JavaScript that processes data from an untrusted source in an unsafe way, usually by writing the data back to the DOM.

DOM based cross-site scripting explained:

  1. Attacker sends malicious script directly to the user
  2. User opens the link and sends HTTP request to the vulnerable site
  3. User’s browser displays the website but JS code modifies the data
  4. Depending on the actual code user data might be sent back to the attacker

DOM based XSS is not necessarily visible on the server side (to the website owner).

example: DOS XSS on WordPress

🔴 CVE-2022-21662 is a Stored Cross-Site Scripting vulnerability which affects WordPress versions up to and including 5.8.2. It allows an attacker with author role to inject a JavaScript payload and later hijack user sessions from Administrators.

How it’s done:

  1. User with author role creates two posts A and B
  2. Changes the slug of A to URL_ENCODED_JS_PAYLOAD+FILLING_CHARACTERS
  3. Changes the slug of B to the same as A
  4. Admin user visits posts page and executes the payload

When setting the slug for the second post B, the payload ends up decoded in the database because there is already a post with the same slug.

For technical details regarding the WordPress 5.8.2 Stored XSS Vulnerability please visit Sonar blog.


Q: How to protect WordPress website from Cross-site scripting attacks (XSS attacks) ?

A: It is important to realize that for preventing XSS attacks a multi-layer approach should be adopted. As attack methods become more advanced, it’s not uncommon for defenses to start failing to protect against the more obscure types of payloads that attackers take advantage of.

Here are some of the most commonly used techniques that can provide a robust defense approach to effectively combat XSS.

For developers:

Sanitize user inputs

Before any user input is used by the application, it should go through a series of checks. These checks, which include filtering user input and encoding user output, should be performed server-side. User agents, content types, cookies, and other parts of a request are the most commonly manipulated. All of these should be regarded as untrusted since they can be exploited by an attacker.

Here is a list of existing WordPress functions that you can use to sanitize your data.

  • sanitize_email
  • sanitize_file_name
  • sanitize_html_class
  • sanitize_key
  • sanitize_meta
  • sanitize_mime_type
  • sanitize_option
  • sanitize_sql_orderby
  • sanitize_text_field
  • sanitize_title
  • sanitize_title_for_query
  • sanitize_title_with_dashes
  • sanitize_user
  • esc_url_raw
  • wp_filter_post_kses
  • wp_filter_nohtml_kses

User input filtering

Filtering user input is another option to consider. For example, eliminating all <script> tags from the data received in a sign-up page’s first name field.

When more advanced payloads are used, developers may try to construct their custom PHP and JS functions, but this might still lead to a vulnerability and so it’s best to use built-in PHP and WordPress core functions.

Built-in PHP functions

  • isset
  • empty
  • mb_strlen
  • strlen
  • preg_match
  • strpos
  • count
  • in_array

Core WordPress functions

  • is_email
  • term_exists
  • username_exists
  • validate_file

Appropriately encode data output

When user-controlled input is being used, the output should be encoded to avoid the browser from misinterpreting it as content to render. The sort of encoding that must be employed depends on the context in which the input is placed.

Escaping helps securing data prior to rendering it for the end user. WordPress has many helper functions you can use for most common scenarios:

  • esc_attr
  • esc_html
  • esc_js
  • esc_textarea
  • esc_url
  • esc_url_raw
  • wp_kses
  • wp_kses_post
  • wp_kses_data

Most WordPress functions properly prepare data for output, so you don’t need to escape the data again. For example, you can safely call the_title() without escaping.


For WordPress users:

Update WordPress, theme and plugins

The most recommended protection method not just for XSS attacks but for almost any other type of attacks is to always use up-to-date versions of PHP, WordPress core, theme and plugins. This ensures that all code is part of a secure development lifecycle (SDLC). Meaning that if there are known vulnerabilities inside earlier versions, they are fixed in newer versions and your website is safe from attacks that exploit those vulnerabilities.

When a new version of a plugin or theme is available, an alert bubble is displayed in your WordPress Admin Menu and the corresponding theme or plugin is highlighted on Themes and Plugins Screens.

Since WordPress 5.5, automatic updates are available and you can enable/disable auto-updates on a plugin-by-plugin and theme-by-theme basis.

By default WordPress runs auto-updates twice per day and sends email notifications when updates happen.


Use a Web Application Firewall

Web Application Firewalls (WAF) provide security against many potential vulnerabilities. They filter malicious traffic and provide intelligent protection to your website blocking most common XSS, SQLi, CSRF, bad bots, OWASP top 10 and other cyber attacks.

Here are some of the best WAF’s to secure your WordPress website:


Block XSS attacks with .htaccess

Further security improvements can be made inside the .htaccess file to block XSS attacks.

Blocking complete JavaScript or HTML payloads is possible with the X-Content-Type-Options and Content-Type headers, meaning if a payload is reflected back the browser will not interpret it, resulting in no JavaScript or HTML being executed. This can be achieved by adding the following code inside your website .htaccess file:

# X-XSS-Protection

<IfModule mod_headers.c>
Header set X-XSS-Protection "1; mode=block"
</IfModule>

To protect against page-framing and click-jacking we can add X-Frame-Options (XFO) security header:

# X-Frame-Options

<IfModule mod_headers.c>
Header always append X-Frame-Options SAMEORIGIN
</IfModule>

NOTE: Theme Customizer might not work as expected with X-Frame-Options SAMEORIGIN so make sure you test it after adding the above code in .htaccess

To protect from content-sniffing add the nosniff header option:

# X-Content-Type nosniff

<IfModule mod_headers.c>
Header set X-Content-Type-Options nosniff
</IfModule>

Was this post helpful?

Leave a Comment