Introduction to WordPress Front End Security: Escaping the Things

Avatar of Andy Adams
Andy Adams on (Updated on )

If you’re a WordPress developer that writes HTML/CSS/JS (which is 100% of theme developers and 99% of plugin developers), you need to know the basics of front end security for WordPress. WordPress gives you all the tools you need to make your theme or plugin secure. You just need to know how and when to use each tool.

For example, one huge responsibility you have as a front-end developer is to prevent unescaped content from being printed to the page.

Security Is Your Responsibility

Before we talk specifics, I’d like to squash an idea that I would (previously) use to rationalize a “less strict” view of front end security:

“If a hacker is already able to change the code/database/content, what difference does it make if my front-end code is secure? Couldn’t they already do more substantial damage?”

Yes, if a hacker already has control of the code or database, chances are they don’t care how well-secured the output is (they can probably change it anyway).

Even so, you still have a responsibility to make the front-end code secure. A few reasons why:

  • Proper front end security also prevents user error from causing big problems. If the user accidentally puts a special character in a field they weren’t supposed to, the page won’t explode.
  • Some attacks are limited. Perhaps the hacker had minimal control and could only change a single piece of content in the database. You can prevent that narrow attack from becoming much larger.
  • Security is an onion. The outermost (often inedible) layer is the front-end display. Good security practices make it that tiny bit harder for a hacker to exploit a site.

Enough with the pep talk. Let’s talk about securing our WordPress themes and plugins.

What Are We Worried About?

A major concern for a front end developer is avoiding Cross-Site Scripting (XSS for short, because ‘CSS’ would cause all sorts of problems. Puns and one-liners welcome.).

XSS vulnerabilities allow bad people (“hackers”) to steal information. Typically that means stealing cookies and session information. If a bad person can send your active session back to themselves (hence the “cross site” part), they could use that to be logged in to your account and do anything you could do (bad news bears).

What is Escaping?

In computer science the word “escape” has a special meaning:

To convert a character or string of characters to be interpreted literally within a specific context, typically to prevent those characters from being interpreted as code.

Put another way, in the context of WordPress front-end development: Escaping changes possibly evil content into safe content.

The biggest danger here is generally <script> tags and other methods of executing JavaScript (e.g. onclick="javascript:"). If a <script> can be output on the page an executed, that’s super dangerous. Escaping means turning that into &lt;script&gt; which is safe.

Why You Need To Escape: An Example

In case you’re not familiar with XSS, a quick example will demonstrate the risk.

Suppose your theme had a field to add a link for further reading. A “Read More” link, if you will. On the WordPress dashboard, it might look like this:

Custom Field

In your theme, you’d like to display this link at the bottom of the post, like so:

Custom Field on the Front End

So, you open up single.php (the file responsible for displaying blog posts) and add the following near the bottom:

<?php 
  $read_more_link = get_post_meta( 
    get_the_ID(), 
    'read_more_link', 
    true 
  ); 
?>

<!-- Don't do this -->
<a href="<?php echo $read_more_link; ?>">Read More</a>

Suppose someone maliciously enters this text into your custom field:

Custom Field With Evil Script

When you visit the page, here’s what you’ll see:

Evil Script Executed!

Yikes! If that bad input allowed a bad person to execute JavaScript, they could:

  • Redirect the user
  • Hijack the user’s cookies
  • Do other bad things

An Escape for Everything

Now that we know how important escaping is, let’s look at the escaping methods WordPress provides and give some context for each.

Function: esc_html

Used for: Output that should have absolutely no HTML in the output.

What it does: Converts HTML special characters (such as <, >, &) into their “escaped” entity (&lt;, &gt;, &amp;).

A common example would be displaying text-only custom fields in a theme:

<?php $dog_name = get_post_meta( $post_id, 'dog_name', true ); ?>
<span class="dog-name"><?php echo esc_html( $dog_name ); ?></span>

Codex entry for esc_html

Function: esc_attr

Used for: Output being used in the context of an HTML attribute (think “title”, “data-” fields, “alt” text).

What it does: The exact same thing as esc_html. The only difference is that different WordPress filters are applied to each function.

Here’s esc_attr used on an image:

<img src="/images/duck.png" 
alt="<?php echo esc_attr( $alt_text ); ?>" 
title="<?php echo esc_attr( $title ); ?>" >

Codex entry for esc_attr

Function: esc_url

Used for: Output that is necessarily a URL. Examples would be image src attributes and href values.

What it does: A more thorough, specific escaping than the esc_attr & esc_html functions, which removes or converts any characters not allowed in URLs into their URL-safe format.

Use esc_url when you need to output a link or dynamic image path:

<a href="<?php echo esc_url( $url ); ?>">Link Text</a>
<img src="<?php echo esc_url( $image_url ); ?>" >

Codex entry for esc_url

Function: esc_js

Used for: Printing JavaScript strings, primarily on inline attributes like onclick.

What it does: Converts special characters like <, >, quotes – anything that could break JavaScript code.

esc_js is probably the least used of the esc_ functions, for a few reasons:

  1. Most JS is loaded in a separate file.
  2. Most JS isn’t written inline as attributes.
  3. For non-inline JS, json_encode is a better option.

But, if you ever need to escape some inline JS, here’s how you’d do it:

<?php $message = get_post_meta( get_the_ID(), 'onclick_message', true ); ?>
<a href="/blog/" onclick="alert('<?php echo esc_js( $message ); ?>')">...</a>

Codex entry for esc_js

Function: json_encode

Used for: Printing a PHP variable for use in JavaScript.

What it does: Converts a PHP variable (object, string, array, whatever) into a sensible, escaped JavaScript representation of that PHP variable.

Useful in particular for <script> blocks making use of WP variables. A simple example:

<

pre rel=”PHP”><?php $categories = get_categories(); ?>

<script type="text/javascript">
var allCategories = <?php echo json_encode( $categories ); ?>;
// Do something interesting with categories here
</script>

PHP reference for json_encode

Function: wp_kses

Used for: Output that needs to allow some HTML, but not all tags or attributes.

What it does: Strips the content of any tags or attributes that don’t match the list of rules passed in.

Use wp_kses if there is a context that allows certain tags (e.g. inline formatting tags like <strong> and <em>) to be printed as HTML.

A basic example would be displaying comments:

, , and tags array( ‘a’ => array( // Here, we whitelist ‘href’ and ‘title’ attributes – nothing else allowed! ‘href’ => array(), ‘title’ => array() ), ‘br’ => array(), ’em’ => array(), ‘strong’ => array() ) ); ?>

Codex entry for wp_kses

What are the _e and _x versions of the escaping functions (esc_attr_e, esc_attr_x)?

These are convenience functions (to make your life easier), and they’re useful when printing translatable strings: text that can be changed with a WordPress translation file.

If you’re developing a theme or plugin for wide distribution (vs. a one-use client project), you’ll want to make every string internationalization-friendly. That means PHP strings that were once under your control become editable by translators – hence the need for escaping (you can’t trust anybody):

Blog

The second parameter to the _e functions is the translation domain. It is used by translators when they write and generate their translations.

The _x functions (like esc_html_x) are essentially the same as their _e counterparts, with an added “context” argument to explain the context a word or phrase is used in. Useful for words with multiple meanings:



  

Codex entry for esc_attr_e
Codex entry for esc_attr_x

What about the_title and the_content?

If you crack open the default WordPress theme (named “Twenty Fifteen” at the time of this writing), you’ll see code like this outputting the title of a post:

‘, ” ); ?>

You’ll also see unescaped calls to the_content like so:

You may be panicking. Why aren’t those functions escaped?! 2 reasons:

1. WordPress automatically escapes them

Functions like the_title are convenience functions – tools that make front-end development easier. As such, to make it even easier for developers, WordPress already automatically escapes the output for the_title Update: WordPress does not escape the_title, be careful there!.

2. Context: Some Content is HTML

In the case of the_content, the output is expected to be unescaped HTML. When the user adds a

to their content, we can assume they actually want a

output to the page – instead of the escaped version.

If you have security concerns with a user being able to add scripts to the_content‘s output, you can use wp_kses to strip unwanted tags from the final output. This would be useful on subdomains and shared hosting, and I’m pretty sure this is what they use on WordPress.com (a hosted version of WordPress where users aren’t allowed to add their own JavaScript).

Handy tip: If you want to use the_title as an HTML attribute, use the_title_attibute to save yourself an escape. the_title_attribute in action:

...

Escape the Things

I work with a lot of plugins and themes – commercial, free, and custom-built – and I see lots of unescaped content (and resulting XSS vulnerabilities). Hopefully, this article will equip you with the basic tools you need to make your WordPress things safer.

Did I overlook anything? Let me know in the comments!