Whitelist validation in WordPress

Far too often when doing code reviews, I see developers lean too heavily on sanitize_text_field(), using it as a sort of catchall for sanitizing data. That’s usually not a good idea. Data should be sanitized based on what sort of data you’re expecting.

In this tutorial, I’m going to cover one method of making sure data is safe that not nearly enough WordPress developers utilize. It’s called whitelist validating, which simply validates a given value against a whitelist of allowed possibilities. When possible, I always prefer this sort of validating because it’s the safest method for making sure data is what it should be.

Whitelist validating is the act of taking a posted value and checking if that value exists within a limited set of allowed values. If the posted value is not in the whitelist, we reject it and return a safe default instead.

A simple example of whitelist validating

Let’s suppose we’re asking a user to input a favorite fruit. We have a limited number of allowed fruit. We’re going to look for the justin_favorite_fruit key.

I’ve broken this down in the following code. It’s simple to do.

// Get the value and remove the slashes that WP adds.
$value = wp_unslash( $_POST['justin_favorite_fruit'] );

// Create an array of allowed values.
$allowed = [
	'apple',
	'banana',
	'orange',
	'watermelon'
];

// Assign the favorite fruit if it's allowed. Fall back to a default.
$favorite_fruit = in_array( $value, $allowed ) ? $value : 'apple';

When to use whitelist validation

Do you have a list of predefined choices? Then, you should use whitelist validation. It’s as simple as that. The following types of settings are when you typically have choices.

  • Select dropdown field.
  • List of checkbox fields.
  • Radio fields.

There are other scenarios where you may want to use whitelist validation. One such example is if you only have a single valid value where you would check if the posted value is an exact match using ===.

Building a real-world scenario

In practice, you’ll likely want to make your code as DRY as possible. Your whitelist should also serve as your choices list. This will allow you to use it both for outputting the form field and for validating. Using our “fruits” example from above, let’s create a function that serves that purpose.

function fruit_choices() {
	return [
		'apple'      => __( 'Apple' ),
		'banana'     => __( 'Banana' ),
		'orange'     => __( 'Orange' ),
		'watermelon' => __( 'Watermelon' )
	];
}

Note that we’ve moved the actual choices to the array keys rather than the array values as in the earlier example. Therefore, you’d need to use either isset() or array_key_exists() for checking if a value is in the whitelist.

Use in the customizer

The following code is an example of adding a “Fruit” section with a “Favorite Fruit” dropdown in the customizer. You can see that we use the fruit_choices() function to provide the choices to our control and in the sanitize_callback for the setting.

add_action( 'customize_register', function( WP_Customize_Manager $manager ) {

	// Add custom section.
	$manager->add_section( 'fruit', [
		'title' => __( 'Fruit' )
	] );

	// Add a favorite fruit setting.
	$manager->add_setting( 'favorite_fruit', [
		'default'           => 'apple',
		'sanitize_callback' => function( $value ) {
			return array_key_exists( $value, fruit_choices() ) ? $value : 'apple';
		}
	] );

	// Add a favorite fruit select dropdown.
	$manager->add_control( 'favorite_fruit', [
		'section' => 'fruit',
		'label'   => __( 'Favorite Fruit' ),
		'type'    => 'select',
		'choices' => fruit_choices()
	] );
} );

Use on a plugin settings page

If you’re building a plugin settings page using the core Settings API, the following should be straightforward. We check the whitelist in the sanitize_callback function for register_setting().

I’m not going to cover how to create a settings page in WordPress with sections and fields. The following code just shows how to set up the whitelist validation aspect.

add_action( 'admin_init', function() {

	register_setting( 'settings_group', 'favorite_fruit', [
		'type'              => 'string',
		'sanitize_callback' => function( $value ) {
			return array_key_exists( $value, fruit_choices() ) ? $value : 'apple';
		}
	] );
} );

Smarter and safer

Whitelist validation means you have to worry far less about a whole host of other issues when sanitizing or validating data. Because you have a predefined list of valid values, you never have to worry about whether someone is sneaking in something fishy.