WordPress 2.0.3+ has some security enhancements that a lot of people are wondering about, so here’s my attempt at explaining them.
Authentication: cookies are your backstage pass
When you sign into WordPress, you are granted a cookie… a little file that lives in your browser and acts as your “backstage pass” to the WordPress admin. This prevents unauthorized people from accessing your admin and doing bad things. They don’t have the cookie, so they’re stopped at the door by the bouncer. Your cookie is tied to your user account, which ties into the WordPress capabilities system which controls what things you can and can’t do in the admin. This is authentication: verifying that the person performing an admin action is authorized to do it.
Intention: the need to protect you from yourself
Say you’re logged in to your WordPress install. You can click links and submit forms that do things to your blog. Things like changing options, deleting posts, creating users, etc. What would happen if someone were to create a link or a form that points to your WordPress admin and attempts to do something malicious? Well, if an unauthorized person submits the form or clicks the link, nothing happens. They get rejected by the browser because they don’t have a login cookie. But what if you were tricked into clicking that link? Uh oh… the bouncer lets you in, and the action is performed. You have authority, but you lacked intention. You didn’t mean to click a link that would delete a post. You were tricked! In order to protect you from this, WordPress needs to make sure that you intend to do the things you do.
HTTP_REFERER is the old way
In the past, WordPress did the following: it verified that the page you were viewing before you initiated the action was a WordPress admin page. That is, you were already inside… the link or the form that you clicked wasn’t malicious; it was part of the WordPress admin. WordPress did this by checking the
HTTP_REFERER value that your browser sent.
Intention: nonces are the way forward
A nonce is a number used once, and it is used for intention verification purposes in WordPress. Think of it as a password that changes each time it is used. WordPress’ implementation isn’t technically a nonce, because the electronic key only changes every 12 hours, and is valid for 24 hours (that is, the current key, and the penultimate key are valid), but it’s close enough. The idea is simple: we verify that user’s request is intentional by verifying that the request originates from within the WordPress admin. And we do that by providing a piece of information (a nonce) to them when that first page is requested. When the action is performed, the piece of information is passed along, and verified. Nonces can be locked down in many ways. They are unique to the WordPress install, to the WordPress user, to the action, to the object of the action, and to the time of the action (24 hour window). That means that if any of these things changes, the nonce is invalid. So if you (somehow) intercept a nonce being used by me, you would, first of all, only have 24 hours to use this key to attempt to trick me. And you’d only be able to use this key to trick me, on my blog, into doing a specific action to a specific item. So even if you got a nonce used to delete a post, the absolute worst case scenario is that you might be able to trick me into deleting that specific post. The nonce is useless for any other purpose.
Nonces: use them in your plugins!
Plugin authors, listen up. In order to prevent the backwards-compatibility “Are you sure?” dialog from coming up when HTTP_REFERER-agnostic users interact with your plugins (especially options pages), you need to add nonce capabilities to your plugins. Don’t worry, it’s way easy.
<form> nonce protection
To protect your
<form> with a nonce, simply use the
wp_nonce_field() function within the
<form> (I’d do it right after the opening tag.) For backwards compatibility, use
function_exists() to execute the code conditionally. The string you pass to the function should be unique to your plugin. Use the following convention:
plugin-name-action_object So, if my plugin is called “cool plugin” and the action the form does is “update options” and the object is “advanced options,” I’d do
cool-plugin-update-options_advanced. The “object” part isn’t required, but if you have multiple forms, protect each one with a different object for the highest level of protection.
<form ...> <?php if ( function_exists('wp_nonce_field') ) wp_nonce_field('plugin-name-action_' . $your_object); ?>
Link nonce protection
If you’re performing actions based on the clicking of links, you can add a nonce to your links using
wp_nonce_url() which takes two parameters. The first is the URL of the link, the second is your nonce key. Note the use of
function_exists() for backwards compatability:
<?php $link = 'your-url.php'; $link = ( function_exists('wp_nonce_url') ) ? wp_nonce_url($link, 'plugin-name-action_' . $your_object) : $link; ?> <a href="<?php echo $link; ?>">link</a>
On the backend, before performing the action protected by the nonce, simply call this:
<?php check_admin_referer('plugin-name-action_' . $your_object); ?>
Feedback and other resources
Please let me know if any of this information (especially regarding plugin nonce implementation) is incorrect and I’ll update it. You should also give Owen’s post on WordPress’ nonce implementation a read.