Home » Web » CSS » WordPress Front End Security: CSRF and Nonces

WordPress Front End Security: CSRF and Nonces

In our last article, we covered Cross-Site Scripting (XSS) and the functions WordPress provides to prevent XSS attacks. Today, we’ll look at another security concern for front end developers: Cross-Site Request Forgery (CSRF).

Lest you think this security stuff isn’t important, a major vulnerability was recently found in the WP SEO plugin, which is installed on 1,000,000+ WordPress sites and which allowed hackers to manipulate the WordPress database using CSRF. (The plugin was fixed quickly, but you can see how scary this stuff can be.)

Nonces and Cross-Site Request Forgery

Put simply, CSRF is when bad guys try to trick users (usually someone with access to the WordPress dashboard) into doing something they didn’t intend to do. A simple example can help illustrate:

Suppose you were building a WordPress plugin to allow logged-in users to submit their pictures. On the front end of the site, your plugin might generate a form like so:

<?php if ( is_user_logged_in() ) : ?>
  <form action="/submit-picture/" method="get">
    <input type="text" name="picture_url">
    <input type="submit">
  </form>
<?php endif; ?>

On the back end, you handle the picture form submissions:

if ( is_user_logged_in() && isset( $_REQUEST['picture_url'] ) ) {
  // Save the picture here
}

Now, suppose a hacker wants to submit a fake picture. The hacker doesn’t have a username or password – so they can’t do anything, right?

Not quite. To hijack one of your users’ accounts to submit a fake picture, the hacker could try to get a logged-in user to click a link that looks like this:

http://your-site.com/submit-picture/?picture_url=fake-picture.jpg

If a logged-in user clicks that link, what would stop the picture from being submitted? You guessed it: Nothing. Hacker wins.

That’s because we didn’t do anything to verify the user’s intention to submit a picture. The “F” in CSRF stands for forgery: The hacker has forged (faked) a request on behalf of the user, kind of like how you forged your mom’s signature on sick notes in elementary school.

How to Prevent CSRF

We can stop CSRF attacks by using some handy functionality built into WordPress. To prevent a request from being successfully “forged”, WordPress uses nonces (numbers used once) to validate the request was actually made by the current user.

The basic process looks like this:

  1. A nonce is generated.
  2. That nonce is submitted with the form.
  3. On the back end, the nonce is checked for validity. If valid, the action continues. If invalid, everything halts – the request was probably forged!

Let’s Add a Nonce

On the front end, suppose we wanted to add a nonce to a form submission. We’ll do this with the wp_nonce_field convenience function:

<form action="/submit-picture/" method="get">
  <input type="text" name="picture_url">
  <input type="submit">
  <?php wp_nonce_field( 'submit_picture' ); ?>
</form>

Easy, right? Now, when we’re processing this form on the back end, we just need to use some built-in WordPress functions to verify that the nonce was valid:

// By default, we can find the nonce in the "_wpnonce" request parameter.
$nonce = $_REQUEST['_wpnonce'];
if ( ! wp_verify_nonce( $nonce, 'submit_picture' ) ) {
  exit; // Get out of here, the nonce is rotten!
}

// Now we can process the submission
if ( is_user_logged_in() && isset( $_REQUEST['picture_url'] ) ) {
  // Save the picture here
}

Because the nonce is unknown to the hacker, he can’t craft a fake link that will do harm. Any malicious attempts will be squashed at the wp_verify_nonce check. We’ve stopped the hacker, right?! For many cases, yes. We’ve made it much more difficult for a hacker to forge a request.

But what if a bad logged-in user of your site wanted to submit a fake picture for another user? The bad user could look at the HTML source of the site, find the nonce field and add it to their “evil” URL like so:

http://your-site.com/submit-picture/?picture_url=fake-picture.jpg&_wpnonce=NONCEVALUE

If another logged-in user clicked that link, they would unwittingly submit the fake URL and it would pass our wp_verify_nonce check.

To stop the bad user, we could use more specific nonces, tied to the user performing the action.

User-Specific Nonces

User-specific nonces are essentially the same as the simple example above, with a small modification to the nonce generation/validation. Here’s what our form looks like with a user-specific nonce:

<form action="/submit-picture/" method="get">
  <input type="text" name="picture_url">
  <input type="submit">
  <!-- The next line is changed -->
  <?php wp_nonce_field( 'submit_picture_for_user_' . get_current_user_id() );  ?>
</form>

Notice that the nonce is generated uniquely for this user. Each logged-in user will get a different nonce, so a hacker will have no way of knowing what nonce value they need to add to their evil URL. Foiled!

Here’s how to validate that same nonce on the back end:

// By default, we can find the nonce in the "_wpnonce" request parameter.
$nonce = $_REQUEST['_wpnonce'];
if ( ! wp_verify_nonce( $nonce, 'submit_picture_for_user_' . get_current_user_id() ) ) {
  exit; // Get out of here, the nonce is rotten!
}

// Now we can process the submission
if ( is_user_logged_in() && isset( $_REQUEST['picture_url'] ) ) {
  // Save the picture here
}

Try These Nonce Functions

WordPress is full of convenience functions to make generating nonces (and preventing CSRF) easier. Here are a few you might find useful for different situations:

Function: wp_verify_nonce

What it does: Validates that the nonce value was legitimately generated by WordPress.

No matter how you generate your nonce, it should be checked on the back end with wp_verify_nonce

Just like the examples above, use wp_verify_nonce to verify a user’s intent:

$submitted_value = $_REQUEST['_wpnonce'];

if ( wp_verify_nonce( $submitted_value, 'your_action_name' ) ) {
  // nonce was valid...
}

Codex entry for wp_verify_nonce

Function: wp_nonce_field

What it does: Prints a hidden <input> tag containing a nonce value.

Used when you’re building a <form> that needs to be protected from CSRF (which is pretty much every <form>).

Just like in the examples above, wp_nonce_field is a convenience function to make our life easier building HTML forms:

<form>
<!-- Other form fields go here -->
<?php wp_nonce_field( 'your_action_name' ); ?>
</form>

Codex entry for wp_nonce_field

Function: wp_nonce_url

What it does: Returns a URL with a nonce value attached.

Used when you’re building a URL that needs a nonce field appended as a query string parameter. Most useful when doing GET requests that need CSRF validation.

Here’s an example of wp_nonce_url where we need to validate that a user intended to click a link:

<?php
  $action_url = wp_nonce_url( '/change-color/?color=blue', 'change_color' );
  // $action_url is now "/change-color/?color=blue&_wpnonce=GENERATED_VALUE"
?>
<a href="<?php echo esc_url( $action_url ); ?>">Change to blue</a>

Codex entry for wp_nonce_url

Function: wp_create_nonce

What it does: Generates a nonce value.

Used when you need a nonce value that doesn’t fit one of the above convenience functions.

Here’s wp_create_nonce used to generate a nonce value for returning in JSON format (useful for returning in AJAX requests):

<?php
  $nonce_value = wp_create_nonce( 'your_action_name' );

  return json_encode( array( 'nonce' => $nonce_value ) );
?>

Codex entry for wp_create_nonce

Function: check_ajax_referer

What it does: Checks for a submitted nonce using wp_verify_nonce, and if the validation fails, stops execution.

check_ajax_referer is another convenience function mainly for use in AJAX requests. You can use it to skip doing the wp_verify_nonce check yourself:

<?php
  // Forged requests won't get past this line:
  check_ajax_referer( 'your_action_name' );

  // Do your action here
?>

Codex entry for check_ajax_referer

Nonces for Everything!

CSRF vulnerabilities range from harmless (e.g. poll submissions) to extremely dangerous (e.g. modifying passwords or permissions).

Regardless of severity, you should be using nonces to prevent CSRF any time a user does an action in WordPress. Because…why wouldn’t you? You don’t want hackers forging requests on your site, and WordPress gives you the tools to stop them.

Use nonces, stop forgery, and foil hackers!


WordPress Front End Security: CSRF and Nonces is a post from CSS-Tricks

Feed Source: CSS-Tricks
Article Source: WordPress Front End Security: CSRF and Nonces

About Admin

Powered by WP Robot