Post Type Archives: Tips

When a user performs an action which alters the database, let it be submitting a settings form, modifying a user’s profile details, or editing a post’s body, we want to confirm it was actually them who initiated the action. WordPress packages a nice system called nonces (“Numbers used Once”) to help you out.

Because nonces have a set expiration, and can be generated to be unique to a given action, they’re easy for WordPress to verify and difficult for the baddies to fake. Without nonces, your code can be susceptible to [cross-site request forgery](http://en.wikipedia.org/wiki/Cross-site_request_forgery).

Back when nonces were added to WordPress, Mark Jaquith [did a nice write-up](http://markjaquith.wordpress.com/2006/06/02/wordpress-203-nonces/). If the introduction is a bit confusing, you should go read that link and then check back.

### Verifying Intent: Nonces

Building upon our [capability example](checking-capabilities.md), when we generate the action link, we’ll want to use `wp_create_nonce()` to add a nonce to the link:

The `wpdd_frontend_delete` argument we pass to the function ensures that the nonce we’re creating is unique to our action. Nonces can also be included in a form with the `wp_nonce_field()` helper function.

Then, when we’re processing a request to delete a post, we check that the nonce is what we expect it to be:

Nonces keep WordPress secure because they verify the user intended to perform the action. Because nonces have an expiration time, and can be generated to be unique to the form or link, it’s very difficult for the baddies to forge them.

When a user performs an action which alters the database, let it be submitting a settings form, modifying a user’s profile details, or editing post data, or accesses private data, we want to check that they have the *capability* to do so. WordPress makes this easier to do through its system of [Roles and Capabilities](https://codex.wordpress.org/Roles_and_Capabilities).

Before we go to much further, let’s review. A *role* is a collection of capabilities. WordPress creates roles like Administrator, Editor, and Author by default — they correlate very much with which permissions you’d want to assign in a publishing workflow. And a *capability* is pretty much that: a defined permission. In its default roles, WordPress uses capabilities like `manage_options`, `edit_posts`, and `switch_themes`. When a WordPress user is created, they’re assigned a role, and the collection of capabilies associated with the role defines what they can do.

### Capabilities: Verifying Permissions

As you’re writing your safe and secure code, you’ll want to pay keen attention to which roles should be permitted to perform which action. In this example, we have a helpful function which gives editors a link to trash posts from the front end of their site:

Each time a user submits data to WordPress, or data is ingested from an external feed, or data generally comes from an external source, you should make sure it’s safe to handle. You want to make sure the data is safe for a variety of reasons; to help prevent XSS if the data is improperly escaped on output, and to ensure your code is executing how you expect are two good reasons. You can make sure this data is safe to use by validating and sanitizing.

If you learn anything from this document, pay attention to you’re handling those $_GET and $_POST variables!

Validating: Checking User Input

Validating is ensuring the data you’re receiving from a user matches what you expect to receive. Let’s take a look at an example.

Say we have an input area in our form like this:

<input type="text" id="my-zipcode" name="my-zipcode" maxlength="5" />

We’re telling the browser to only allow up to five characters of input, but there’s no limitation on what characters they can input. They could enter “11221” or “eval(“. We want to make sure we’re only processing zip codes from the form.

This is where validation plays a role. When processing the form, we’ll write code to check each field for its proper data type. If it’s not of the proper data type, we’ll discard it. For instance, to check “my-zipcode” field, we might do something like this:

Since the maxlength attribute on our input field is only enforced by the browser, we still need to validate the length of the input on the server. If we don’t, an attacker could cleverly submit a form with a longer value.

The intval() function casts user input as an integer, and defaults to zero if the input was a non-numeric value. We then check to see if the value ended up as zero. If it did, we’ll save an empty value to the database. Otherwise, we’ll save the properly validated zipcode.

This style of validation most closely follows WordPress’ whitelist philosophy: only allow the user to input what you’re expecting.

Sanitizing: Cleaning User Input

Sanitizing is a bit more liberal of an approach to accepting user data. We can fall back to using these methods when there’s a range of acceptable input.

The sanitize_*() class of helper functions are super nice for us, as they ensure we’re ending up with safe data and require minimal effort on our part:

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()

Conclusion

Any time you’re using potentially unsafe data, it never hurts to validate and sanitize it. Validating is confirming the data is what you expect it to be. Sanitization is a more liberal approach to cleaning your data.

Every time a post title, post meta value, or some other data from the database is rendered to the user, we need to make sure it’s properly escaped. Escaping helps us prevent issues like malformed HTML or the dreaded cross-site scripting attack.

For ease of code review, it’s best to escape as late as possible. WordPress’ escaping functions have low overhead, so there’s no performance penalty to using them as many times as you need to.

Escaping: Securing Output

WordPress thankfully has a few helper functions we can use for most of what we’ll commonly need to do:

esc_html() we should use anytime our HTML element encloses a section of data we’re outputting.

<h4><?php echo esc_html( $title ); ?></h4>

esc_url() should be used on all URLs, including those in the ‘src’ and ‘href’ attributes of an HTML element.

Formatting email newsletters is really hard. When using a RSS feed to deliver content to an email newsletter, you may need to ensure inline images are restricted to a specific width.

The following code snippet constrains images in RSS feed content to 600 pixel width. When an image is larger than 600px wide, it scales the image’s attributes proportionally to fit within 600px. Images already 600px wide or less are ignored.

This approach is also non-destructive. Because the approach filters the rendered output, and not the content in the database, it’s safe to change (or remove) at any future date.

Amazon has a helpful ItemLookup API for fetching details about a given product. Authenticating your request is a bit tricky though. It requires signing your request with a signature. Here’s an overview to how it works in WordPress.

One common strategy for improving WordPress’ performance is to use a service like Redis or Memcached as a persistent storage backend for the WP Object Cache. This lets you more easily cache the result of expensive data generation.

However, there are cases in which you don’t necessarily want all of your object cache to end up in the persistent storage backend. For instance, if you have hundreds of cache calls on a pageload and each call takes 1 ms of network requests, you may want to prevent some of them from occurring. Enter: non-persistent groups.

First, make sure your calls to wp_cache_add( $key, $value, $group ); and wp_cache_set( $key, $value, $group ); include the third group argument. Then, to mark a group as non-persistent, use wp_cache_add_non_persistent_groups( $group_name );.