Brad WIlliams on Secure WordPress Development
This guest post is a technical by Brad Williams, a leading WordPress developer and security expert, as well as a co-founder of WebDevStudios, one of the top WordPress agencies. He is the author of Professional WordPress, and also co-hosts the DradCast.
One of the most important steps when writing code, regardless of what platform the code will run on, is making sure it is secure from hacks and exploits. Running a plugin with a security hole could open up the entire WordPress website to malicious hackers. WordPress features some built-in security tools that you should always take advantage of when creating custom plugins and themes in WordPress to verify your code is as secure as it can be.
Trust No One
The golden rule when writing code is to trust no one. That is, consider all data invalid unless it can be proven valid. Any data that can be manipulated by a third party should be validated and sanitized prior to processing that data. Forgetting this simple rule could end in disaster for anyone running your code.
Data Validation and Sanitization
Any and all data that comes from somewhere external to your code, like user input, needs to be scrubbed to verify it’s free from illegal characters and potentially unsafe data. WordPress contains a set of escaping functions that you can use to verify that your data is escaped properly when displaying it to the screen.
- esc_html() – Used for escaping data that contains HTML. The function encodes special characters in their HTML entities, making it safe to display on the page.
Example:<?php echo esc_html( $text ); ?>
- esc_attr() – Used for escaping HTML attributes. This function should be used whenever you need to display data inside an HTML element
Example:<input type="text" name="name" value="<?php echo esc_attr( $text ); ?>" />
- esc_textarea() – Used for escaping HTML <textarea> values. This function should be used to encode text for use in a <textarea> form element.
Example:<textarea name="bio"><?php echo esc_textarea( $bio); ?></textarea>
- esc_url() – Used for validating and sanitizing URLs. This function should be used to scrub the URL for illegal characters and encodes HTML entities. Also see esc_url_raw(), which uses esc_url(), but does not replace entities for display.
Example:<a href="<?php echo esc_url( $url); ?>">Link</a>
- esc_js() – Used to escape text strings in JavaScript.
Example:<script>var bwar='<?php echo esc_js( $text ); ?>';</script>
If you are working with integers there are two functions you should be using:
- intval() – PHP function to verify that the value is an integer. If the variable is a string, and therefore not an integer, it will return a 0.
Example:<input type="text" name="number_to_display" value="<php echo intval( $number ); ?>" />
- absint() – WordPress function to verify that the value is a non-negative integer. If the variable is a string, or a negative number, it will return a 0.
Example:<input type="text" name="number_to_display" value="<php echo absint( $number ); ?>" />
As important as escaping is when displaying data, sanitizing is when saving data. Let’s look at some of the common sanitization functions that WordPress includes.
- sanitize_text_field() – Used to sanitize standard text data. This function will remove invalid UTF-8 characters, convert single < characters to entity, strip all tags, remove line breaks, tabs and extra white space, and strip octets.
- sanitize_email() – Used to sanitize an email address. This function will strip out all characters that are not allowed in an email address.
- wp_kses() – A very powerful function for sanitizing untrusted HTML. This function verifies only defined HTML tags and attributes are allowed and everything else is stripped out.
- wp_kses_post() – Very similar to wp_kses(), but you do not need to provide an array of allowed HTML tags and attributes. That list is already set based on the allowed HTML tags for regular post content in WordPress.
Let’s look at an example using the wp_kses() WordPress function:
[php]
<?php
$allowed_tags = array(
‘strong’ => array(),
‘a’ => array(
‘href’ => array(),
‘title’ => array()
)
);
$html = ‘<a href=”#” class=”external”>link</a>. This is <b>bold</b> and <strong>strong</strong>’;
echo wp_kses( $html, $allowed_tags );
?>
[/php]
The first step is to define an array of all HTML tags and attributes that are allowed. In the code above you are allowing the <strong> and <a> tags. The <a> tag is allowed to include the href and title attributes. Next, you build an $html variable to run through the wp_kses() function. Let’s look at the output:
[php]
<a href=”#”>link</a>. This is bold and <strong>strong</strong>
[/php]
Notice the <b></b> tags have been completely removed. The function also removed the class attribute from the <a> tag because you didn’t specify that as an allowed attribute. It’s easy to understand how powerful and important the wp_kses() function is in WordPress.
To learn more about escaping and sanitizing in WordPress visit the Data Validation Codex page.
Nonces
Nonces, which stands for number used once, are used in requests (form submissions, ajax requests, saving options) to stop unauthorized access by generating a secret key. This key is generated prior to generating a request, like a form post. The key is then passed in the request to your script and verified to be the same key that was generated. If the key does not match, or does not exist, the entire process will be killed. Let’s look at a basic example:
[html]
<form method=”post”>
<?php wp_nonce_field( ‘williamsba_settings_form_save’, ‘williamsba_nonce_field’ ); ?>
Enter your name: <input type=”text” name=”text” /><br />
<input type=”submit” name=”submit” value=”Save Options” />
</form>
[/html]
As you can see we have a very basic HTML form with a single text field for the user’s name. We are also using the WordPress function wp_nonce_field() to generate a secret key. This key is generated as a hidden form field and passed through the form when it is posted.
Now that you have generated a nonce field in your form, let’s look at the process of verifying the secret key upon form submission:
[php]
function bw_update_options() {
if ( isset( $_POST[‘submit’] ) ) {
//check nonce for security
check_admin_referer( ‘williamsba_settings_form_save’, ‘williamsba_nonce_field’ );
//nonce passed, now do stuff
}
}
[/php]
Verifying that the nonce is valid is as simple as calling the check_admin_referer() function. Simply pass it your unique nonce action and name that you defined earlier. If the secret key does not match WordPress will stop processing the page and issue an error message.
Nonces can also be used on links that perform actions in the form of a querystring. Here’s an example:
[php]
<?php $link = ‘http://example.com/wp-admin/my-url.php?action=delete&ID=15’; ?>
<a href=”<?php echo wp_nonce_url( $link, ‘williamsba_nonce_url_check’ ); ?>”>Delete</a>
[/php]
In this example you’ll use the wp_nonce_url() function to generate a unique secret key in the URL. The function accepts two parameters: the URL to add the nonce to and the unique nonce name you are creating. You can verify the nonce is correct just like you did with your form using the check_admin_referer() function:
[php]
function bw_update_options() {
if ( isset( $_GET[‘action’] ) ) {
//check nonce for security
check_admin_referer( ‘williamsba_nonce_url_check’ );
//do stuff
}
}
[/php]
Understanding how to write solid secure code in WordPress is an absolute necessity in this day and age. One single user-submitted value that is unsanitized could potentially destroy your entire site. Scared? You should be! Now go update your code to be as secure as possible!
Awesome article, I’m going to have to put this in my permanent resources and sent to bad plugin writers. These are such simple techniques and luckily most of the popular plugins have implemented these but you still catch a few that haven’t.
For site owners: REVIEW YOUR PLUGIN CODE, make sure these sorts of things are implemented in plugins you are using.
Very useful, in fact I was looking up all of this recently across a couple of books and this post hit the spot with everything in one place, as ever Brad just awesome!
Great article and very usefull. I will try to use these ideas to make my blog safer!
Thank you Brad for the great summary! wp_kses() function sounds very useful, good to learn these things.
Could anyone recommend a good resource on understanding NONCE better in more detail?
Many thanks!
Dasha
Awesome article, i follow this in my blog and i also obey this.
thanks
Some of the plug-ins can be difficult to understand,
but these are optional and selected by the blogger. 08 AUD per
month for system backup, daily malware scans and security threat
fixes. For any business that would like improve its presence on the Internet, choosing the right Content.
Hi Brad,
very happy to seize and that he shares my view that security is paramount and people who write code need to take that into consideration.
I consider code security essentially the foundation web development. Your company’s work with WPEngne & Sucuri shows you care I do a lot with FireHost, Sucuri & WP engine I need a full time developer I have my current developer is moving to a job that I am happy he obtained because we are close friends however I will now need to seek out a person is capable and that it is not easy. Any advice would be very welcomed.
Thomas
Hi
Thanks for providing this article with us. Very useful information to give us.
Regards
Web Development Company in Chennai