Categories
WordPress Templates

WordPress Templating: WordPress Methods for Data Security

For this demonstration, please install this duplicator archive package. Be sure to make yourself a new admin user account as part of this process.

This is a variation on The Eatery exercise, with two custom post types: meal_menu and staff_member.

In class, we’ll go through a few examples to show ways we can make our template code more secure.

Key to writing secure themes are the following slightly paraphrased points from the WordPress Theme Handbook section on Theme Security

  • Don’t trust any data from users, third party APIs, or data in your database. Always validate on input and sanitize (escape) data before use or on output.

A famous starter theme widely used by WordPress developers is known as underscores (or _s). At the underscores website, underscores.me, you can supply a theme name, theme slug, author name, author url, and author description and then download a complex starter theme to use as a foundation for your project. You can even choose a sass or non-sass scheme for the theme.

Even if you don’t have plans to base a theme on underscores, looking at its good is time well spent: you will learn a lot about theming best practices.

One of the things you will notice right away when looking through the underscores code is the use of escaping functions.

Escaping & Sanitizing

Escaping is the process in which unwanted data, such as HTML or script tags, is converted to a less harmful representation.

This is to ensure that this data is not interpreted by the browser as code.

Sometimes, escaping will prevent an attack. Sometimes, it will prevent user error causing issues with the site. Both are good things.

A case of the former could be when a malicious user inputs JavaScript into a field, in the Dashboard, or into inputs on the front end. An example of the former could be a user entering poorly formed code into an input field.

Related to escaping is sanitization. The difference between escaping and sanitizing is that escaping is applied on output, while sanitization is is applied on input.

As an example of sanitization, while logged in as an admin, make a new post.

Give it this as a title:

<script>alert('enter your credit card number'); </script>

Save the post and then preview it. The JavaScript will run.

Now log into your newly installed site using the username walterwhite and the password walterwhite.

Make a post as walterwhite. Use this as a title to the post:

<script>alert('enter your bank card number'); </script>

Save the post. Even before you preview the page, you’ll notice that the post title has been stripped of the <script> tags. If you do preview, you’ll see that the script does not run.

What we see here is that WordPress’s functions imply differing levels of trust of users, and that there are differing levels of sanitizing applied to user generated content.

That’s on input, though.

We can prevent this Javascript from running regardless of user access level, by escaping on output.

Escape Example 1

Edit the following theme file: content.php, which is at present a very basic file indeed: it’s outputting a title and a category.

Change this:

  <h2 class="entry-title">
    <?php the_title(); ?>
  </h2>

to this:

  <h2 class="entry-title">
      <?php echo esc_html(get_the_title()); ?>
  </h2>

Save the file and in the admin account preview the page with the JS in the title. You should see this:

Rather than the script tags brackets ( < and > ) being interpreted as code, they are converted into entitles, rendering them harmless: they are read as text, not as code. In fact, they are not the only things converted.

To see what I mean by converted to entities, in Chrome go View > Developer > View Source. (You will not see this if you view the source in the Inspector).

Then search for credit card. You will find it a few times, including in the H2 with a class of entry-title:

 <h2 class="entry-title">
    <script>alert('enter your credit card number'); </script>  
</h2>

The code changes we did were the following:

  • Used get_the_title() rather than the_title(): this returns the title, but does not output it to the screen
  • Wrapped the returned data from get_the_title() in an esc_html function: this function converts into entities (symbols) characters we don’t want interpreted as code.
  • Echoed that escaped title and html.

Escape Example Two

At the bottom of the front page of the site is a link to the staff member archive. Follow the link and you’ll see a bunch of articles, one for each staff member.

At the bottom of each article is a social media site name and url. These are both custom fields.

Open up content-staff-member.php for editing. If you look at the last two DIVs in the article, you’ll see we’re outputting a couple custom fields.

If you open for editing one of the Staff Member custom post types in the Dashboard, you’ll notice the custom fields.

Let’s use both of these fields to make a link, so that the name of each site is wrapped in an anchor tag with an href value of the site url.

Make something like this below the last div:

  <div class="staff-member-social-link">
    <a href="<?php the_field('social_media_url_1'); ?>">
      <?php the_field('social_media_title_1'); ?>
    </a>
  </div>

Now delete the original two outputs (the site name and the url). Check that the links work on the staff member articles.

In order to prevent any ill-formed or malicious content, change the code to the following:

<div class="staff-member-social-link">
<a href= 
"<?php esc_url(get_field('social_media_url_1')); ?>" 
>

<?php 
echo esc_html(get_field('social_media_title_1')); ?>
</a>
</div>

Other Potential Examples

The above examples included scenarios like these:

  • a rogue administrator on the site adds javascript inline
  • other users input malformed html into input field

Item one involves intentional malicious code execution. Item two is just user error (but that’s plenty common, of course).

Additional scenarios that escaping provides security against include the following:

  • privilege escalation hacks: non-admin users acquiring admin privileges via hacks or bugs
  • plugins that change output via filters
  • attacks that steal your session information, allowing others to perform actions on the site as you, or to redirect you. These are referred to as cross-site-scripting (xss) attackts.
  • compromised plugins
  • incorrect permissions on wp installation folder allowing for modification of WordPress core files.
  • browser vulnerabilities
  • etc, etc.

The Onion Metaphor

A number of people have spoken of security as an onion. Every layer of security that’s added makes it just a little bit harder for hackers or user error to break or hijack your site.

Many common methods of automated attack aim to modify core WordPress files. The core WordPress files, in other words, represent a fixed target, which means that it is easier to script an attack on them.

So, if a hack has changed what content is output by, say, the_title, escaping the_title in your theme provides some protection against such methods.

WordPress Escaping Methods

esc_html:
Used for content that should have no HTML in the output.
This method converts HTML special characters or invalid characters into entities.

The idea here is to make code that the browser will output as text but not interpret as code.

This escaping of potentially problematic characters, then, helps you avoid unintentional errors as code inserted into other code is not interpreted as code but rather displayed to the screen as text.

esc_attr:
Used for content that will be used as attribute values (such as alt, data-, title, etc attributes).

This method does the same thing esc_html does, but for use in attributes.

esc_url:
Used for urls, as you might guess from the name. This converts characters not allowed in a URLs (such as spaces, lots of other characters) into their URL-safe counterparts.

For more information, an excellent introduction to this topic is found at CSS-Tricks.

And here is a superb listing of which WP core functions should be escaped, and how:
https://developerka.org/2018/05/30/escape-core-functions-wporg-themes/

Finally, here is a Loom series I made recently showing a range of examples, including one method (wp_kses) not discussed in this article.