A Guide to Writing Secure Themes – Part 2: Validation

Validation is a technique to ensure that input is secure before using it in your code.

When validating data, you are verifying that it corresponds to what the program needs. This only works if you have a list of criteria that you can check to determine that the data is valid.

Whitelisting

The simplest validation method is whitelisting. This only works when there is a precise set of possible values that the data can have.

Let’s look at how whitelisting can be used for validating a theme option controlling the position of the sidebarSidebar A sidebar in WordPress is referred to a widget-ready area used by WordPress themes to display information that is not a part of the main content. It is not always a vertical column on the side. It can be a horizontal rectangle below or above the content area, footer, header, or any where in the theme..

image

Here is the code that we used to create the setting and the control:

$wp_customize->add_setting( 'sidebar-position', array(
    'default'           => 'left',
    'sanitize_callback' => 'wptrt_validate_sidebar_position',
) );

$wp_customize->add_control( 'sidebar-position-control', array(
    'label'    => esc_html__( 'Sidebar Position', 'wptrt' ),
    'section'  => 'theme',
    'settings' => 'sidebar-position',
    'type'     => 'radio',
    'choices'  => array(
        'left'  => esc_html__( 'Left', 'wptrt' ),
        'right' => esc_html__( 'Right', 'wptrt' ),
) ) );

The user only has two choices: left or right. This means that in the wptrt_validate_sidebar_position(), we can determine whether the submitted option is one of the two possible values.

function wptrt_validate_sidebar_position( $sidebar_position ) {
    if ( in_array( $sidebar_position, array( 'left', 'right' ), true ) ) {
        return $sidebar_position;
    }
}

To do this, we use the in_array() PHPPHP PHP (recursive acronym for PHP: Hypertext Preprocessor) is a widely-used open source general-purpose scripting language that is especially suited for web development and can be embedded into HTML. http://php.net/manual/en/intro-whatis.php. function. This function returns true when the needle, the submitted value for the position of the sidebar, is in the haystack, the list of possible positions.

The third parameter of the in_array() function is to enable strict type comparison. We pass true as an argument, to enable the strict checking. This is important, because in PHP loose type comparison can lead to unexpected results.

So whitelisting simply means that we compare the submitted data against a list of acceptable values. This works well for controls such as checkboxes, radio buttons, selects, and dropdowns.

But how can we validate data for which we don’t know the possible values? Let’s have a look at validating data according to a set of qualifications.

Qualifying data

When qualifying data, we try to find out whether it meets a precise set of criteria. Let’s look at an example of validating data.

Imagine that you have a metaMeta Meta is a term that refers to the inside workings of a group. For us, this is the team that works on internal WordPress sites like WordCamp Central and Make WordPress. box that allows users to enter a value for the width (in pixels) of the content area of a particular post. While not being a super useful feature in a theme, this example allows us to demonstrate the use of filter_input().

The filter_input() PHP function gets a variable and validates it. The function accepts four arguments: the type of input, the name of the variable to get, the filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. (validation) to apply, and an optional array of options.

<?php
$content_area_width = filter_input(
                INPUT_POST,
                'content_area_width',
                FILTER_VALIDATE_INT,
                array( 'options' => array(
                    'default'   => 500,
                    'min_range' => 100,
                    'max_range' => 1000,
                ) )
            );
?>

Although the code for this function might seem verbose, it’s much shorter and clearer than writing it all out:

<?php
// Warning: This code does not work correctly.
if ( isset( $_GET['content_area_width'] ) && is_int( $_GET['content_area_width'] ) && $_GET['content_area_width'] >= 100  && $_GET['content_area_width'] <= 1000 ) {
    $content_area_width = $_GET['content_area_width'];
} else {
    $content_area_width = 500;
}
?>

You might wonder why there is a warning about this code not working. Seems to look good, right? The problem is that is_int( $_GET['content_area_width'] ) will always return false, so this code will always return 500.

This is because data retrieved from the $_GET and $_POST super globals is always of the type string. Using the filter_input() function allows us to get around this limitation of the PHP language.

Choosing the right qualifications

When validating data, it’s crucial that you choose the right set of qualifications, and express this correctly in the code.

Imagine that you have a CustomizerCustomizer Tool built into WordPress core that hooks into most modern themes. You can use it to preview and modify many of your site’s appearance settings. setting in your theme for entering a link to a Twitter profile. You want to have a valid URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org for this setting, so you use the filter_var() PHP function with the FILTER_VALIDATE_URL filter.

<?php
// Warning: Insecure code!
function wptrt_validate_twitter_profile_url( $url ) {
    return filter_var( $url, FILTER_VALIDATE_URL ) );
}
?>

The next thing you do is output the validated URL in your theme:

<?php
// Warning: Insecure code!
echo '<a href="' . $twitter_url . '">' . esc_html__( 'Twitter', 'wptrtp' ) . '</a>';
?>

In the four lines of code that we have seen so far, we have made two crucial mistakes:

  1. We trusted the filter_var() function to validate the URL to the Twitter profile.
  2. We didn’t escape the URL on output.

We are going to look at escaping in a later part of this series. For now let’s look at why the validation was too weak to be secure.

The problem is that if you enter javascript://test%0Aalert(321), this is a valid URL. As soon as a user would click on the Twitter link on the front end of the site, a JavascriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. dialog would appear.

We need to add additional checks to our function:

function wptrt_validate_twitter_profile_url( $url ) {
    if ( 0 !== strpos( $url, 'https://twitter.com/' ) ) {
        return;
    }

    return filter_var( $url, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED ) );
}

This function now verifies that the data meets three qualifications:

  1. The URL starts with https://twitter.com/.
  2. The URL is valid according to the RFC 2396 standard.
  3. The URL has a path component (as in http://example.org/path).

Validation functions

WordPress validation functions

WordPress only has a couple of validation functions.

  • is_email(): Checks whether the data is a valid email address. The validation done by the function does not comply with the RFC 822 standard, and does not work with internationalized domain names.
  • wp_validate_boolean(): Despite the name, this function not only validates, but also sanitizes the data passed to it. So the return value will always be a boolean. You can use filter_var( $var, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE ) as an alternative, as it returns NULL when the passed data is not valid.
  • sanitize_hex_color(): This actually a validation function, as it returns null if the color code isn’t valid. It is only available in the Customizer context, but it’s a small function so you can copy the code to your own validation function if needed.
  • sanitize_hex_color_no_hash(): The same as sanitize_hex_color() but for values without a leading #.

PHP validation functions

PHP offers a number of validation functions. As we have seen previously, using them can be a bit tricky. So make sure to read the documentation carefully, including the notes.

  • is_bool(): Returns true if the passed variable is of the type boolean.
  • is_float(): Returns true if the passed variable is of the type float.
  • is_int(): Returns true if the passed variable is of the type integer.
  • is_numeric(): Returns true if the passed variable contains a numeric value. Keep in mind that this encompasses all numeric values, so signs, hexadecimal, binary, and octal values are all valid.
  • strtotime(): Not a validation function strictly speaking, but can be used as such to validate dates. The function returns false if the passed data cannot be converted into a timestamp.

Next we have a family of functions that have been specifically designed to validate data.

  • filter_input(): Retrieves an external variable (from $_GET, $_POST, $_SERVER,…) and applies the specified filter.
  • filter_input_array(): Works the same as filter_input(), but allows multiple values to be retrieved with one call.
  • filter_var(): Filters the variable passed as an argument.

When using these functions, you need to indicate a filter to use. The validation filters can be combined with flags to achieve a specific behavior. Some filters also accept additional options.

It’s the combination of the right filter, with the right flags, and the right options that makes these functions do their work correctly.

Conclusion

Now that we have a solid grasp on how validating data works, we’ll look at sanitization in the next part of this series.

#writing-secure-themes