5 Oct 2023

The WordPress Function sanitize_text_field() Isn’t Always Enough Security to Protect Against XSS

The Automattic owned WPScan recently claimed a serious persistent cross-site scripting (XSS) vulnerability had been in a WordPress plugin and had been fixed. Their report lacked the kind of information that would be needed to easily recheck things. What was included didn’t seem promising. For example, they misspelled the word unauthenticated as “Unauthitncated”, which a spellchecker would have caught. Checking over things, we found the vulnerability did exist, but was incompletely fixed and is still exploitable. WPScan claims to have a “dedicated team of WordPress security experts”, so either there is widespread misunderstanding of a basic element of securing a WordPress plugin or they don’t really have that team. Assuming the former, let’s look at what they and the developer got wrong involving usage a WordPress security function sanitize_text_field().

(Two other providers, Patchstack and Wordfence, who also claim to have experts generating their data, are also claiming this has been fixed despite the incomplete fix.)

Persistent XSS involves user input containing malicious code, usually JavaScript code, which is saved and then output. Some combination of sanitization, validation, and escaping is used to avoid that.

The update to the plugin attempting to fix the vulnerability involved passing user input submitted through the plugin to the previously mentioned WordPress function sanitize_text_field(). That works if that input is going to be output as text on a page, but doesn’t work if the input is output as an HTML attribute.

The proof of concept for exploiting this involved the malicious payload being in user input that is output both as text and as an HTML attribute. The proof of concept was designed for exploiting it where it is output as text. So the change made addressed the exploitation the proof of concept allowed, but actual experts understand that they can’t simply check if the proof of concept works, but also if the change being made should fully address the issue, which WPScan didn’t.

There are different way to address a situation like this depending on what the user input is.

Validation

One way to handle this is to use validation. If, for example, the user input should also be an integer, then restricting the value to that stops any possibility of their being malicious code in it.

That is the best option if the range of characters allowed will restrict malicious code from being included, but that isn’t always possible. That is where escaping comes in.

You can find more on validation in WordPress’ documentation.

Escaping

Escaping outputs user input in a way that avoids malicious code from being able to run. WordPress provides different escaping functions for outputting user input in different locations.

In this situation, the user input is being output as an HTML attribute, which as we previously mentioned sanitize_text_field() doesn’t handle. Here is the code where the value is output (as the variable $cltranslate)”:

<div class="af2_post_table_content <?php _e($hide_column) ; ?>" style="width: <?php _e($table_column['width']) ; ?>; flex: <?php _e($table_column['flex']); ?>" data-searchfilter="<?php _e($table_column['lable']); ?>"
                            data-searchvalue="<?php _e($cltranslate); ?>">

The value is being passed through another WordPress function, _e(), which displays translated text, but doesn’t do any escaping.

The way to handle that situation is to use the relevant escaping for an HTML attribute like that, esc_attr(). Since this code uses _e() for the output, the esc_attr_e() variant would be used:

<div class="af2_post_table_content <?php _e($hide_column) ; ?>" style="width: <?php _e($table_column['width']) ; ?>; flex: <?php _e($table_column['flex']); ?>" data-searchfilter="<?php _e($table_column['lable']); ?>"
                            data-searchvalue="<?php esc_attr_e($cltranslate); ?>">

The easiest way to make sure that user input is escaped when output is to use escaping more broadly. So in the above code that would be added to the other variables also being output as well.


Plugin Security Scorecard Grade for Patchstack

Checked on March 5, 2025
D

See issues causing the plugin to get less than A+ grade


Plugin Security Scorecard Grade for WPScan

Checked on April 12, 2025
F

See issues causing the plugin to get less than A+ grade

2 thoughts on “The WordPress Function sanitize_text_field() Isn’t Always Enough Security to Protect Against XSS

Leave a Reply to Plugin Vulnerabilities Cancel reply

Your email address will not be published.