{"id":197463,"date":"2015-03-24T05:00:02","date_gmt":"2015-03-24T12:00:02","guid":{"rendered":"http:\/\/css-tricks.com\/?p=197463"},"modified":"2015-03-26T09:21:36","modified_gmt":"2015-03-26T16:21:36","slug":"wordpress-front-end-security-csrf-and-nonces","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/wordpress-front-end-security-csrf-and-nonces\/","title":{"rendered":"WordPress Front End Security: CSRF and Nonces"},"content":{"rendered":"
In our last article<\/a>, 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). <\/p>\n <\/p>\n Lest you think this security stuff isn’t important, a major vulnerability was recently found in the WP SEO plugin<\/a>, 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.)<\/p>\n 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:<\/p>\n 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:<\/p>\n On the back end, you handle the picture form submissions:<\/p>\n Now, suppose a hacker wants to submit a fake<\/em> picture. The hacker doesn’t have a username or password – so they can’t do anything, right? <\/p>\n 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:<\/p>\n If a logged-in user clicks that link, what would stop the picture from being submitted? You guessed it: Nothing<\/strong>. Hacker wins.<\/p>\n That’s because we didn’t do anything to verify the user’s intention<\/strong> 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<\/strong> your mom’s signature on sick notes in elementary school.<\/p>\n We can stop CSRF attacks by using some handy functionality built into WordPress. To prevent a request from being successfully “forged”, WordPress uses nonces<\/a> (numbers used once) to validate the request was actually made by the current user.<\/p>\n The basic process looks like this:<\/p>\n On the front end, suppose we wanted to add a nonce to a form submission. We’ll do this with the wp_nonce_field<\/a> convenience function:<\/p>\n 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:<\/p>\n 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 But what if a bad logged-in user<\/strong> of your site wanted to submit a fake picture for another user<\/strong>? Couldn’t the bad user could just look at the HTML source of the site, find the nonce field, and add it to their “evil” URL like so?<\/p>\n Fortunately, it won’t work<\/strong>. WordPress nonces are unique to a user’s session<\/a>, so a hacker couldn’t substitute his own nonce for another user.<\/p>\n Hat tip to Mark Allen<\/a> in the comments for pointing out session-unique nonces.<\/p>\n 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:<\/p>\n What it does:<\/strong> Validates that the nonce value was legitimately generated by WordPress.<\/p>\n No matter how you generate your nonce, it should be checked on the back end with Just like the examples above, use Codex entry for wp_verify_nonce<\/a><\/p>\n What it does:<\/strong> Prints a hidden Used when you’re building a Just like in the examples above, Codex entry for wp_nonce_field<\/a><\/p>\n What it does:<\/strong> Returns a URL with a nonce value attached.<\/p>\n 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.<\/p>\n Here’s an example of Codex entry for wp_nonce_url<\/a><\/p>\n What it does:<\/strong> Generates a nonce value.<\/p>\n Used when you need a nonce value that doesn’t fit one of the above convenience functions. <\/p>\n Here’s Codex entry for wp_create_nonce<\/a><\/p>\nNonces and Cross-Site Request Forgery<\/h3>\n
<?php if ( is_user_logged_in() ) : ?>\r\n <form action=\"\/submit-picture\/\" method=\"get\">\r\n <input type=\"text\" name=\"picture_url\">\r\n <input type=\"submit\">\r\n <\/form>\r\n<?php endif; ?><\/code><\/pre>\n
if ( is_user_logged_in() && isset( $_REQUEST['picture_url'] ) ) {\r\n \/\/ Save the picture here\r\n}<\/code><\/pre>\n
http:\/\/your-site.com\/submit-picture\/?picture_url=fake-picture.jpg<\/code><\/pre>\n
How to Prevent CSRF<\/h3>\n
\n
Let’s Add a Nonce<\/h3>\n
<form action=\"\/submit-picture\/\" method=\"get\">\r\n <input type=\"text\" name=\"picture_url\">\r\n <input type=\"submit\">\r\n <?php wp_nonce_field( 'submit_picture' ); ?>\r\n<\/form><\/code><\/pre>\n
\/\/ By default, we can find the nonce in the \"_wpnonce\" request parameter.\r\n$nonce = $_REQUEST['_wpnonce'];\r\nif ( ! wp_verify_nonce( $nonce, 'submit_picture' ) ) {\r\n exit; \/\/ Get out of here, the nonce is rotten!\r\n}\r\n\r\n\/\/ Now we can process the submission\r\nif ( is_user_logged_in() && isset( $_REQUEST['picture_url'] ) ) {\r\n \/\/ Save the picture here\r\n}<\/code><\/pre>\n
wp_verify_nonce<\/code> 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. <\/p>\n
Nonces are User-Specific<\/h3>\n
http:\/\/your-site.com\/submit-picture\/?picture_url=fake-picture.jpg&_wpnonce=NONCEVALUE<\/code><\/p>\n
Try These Nonce Functions<\/h3>\n
Function: wp_verify_nonce<\/h4>\n
wp_verify_nonce<\/code><\/p>\n
wp_verify_nonce<\/code> to verify a user’s intent:<\/p>\n
$submitted_value = $_REQUEST['_wpnonce'];\r\n\r\nif ( wp_verify_nonce( $submitted_value, 'your_action_name' ) ) {\r\n \/\/ nonce was valid...\r\n}<\/code><\/pre>\n
Function: wp_nonce_field<\/h4>\n
<input><\/code> tag containing a nonce value.<\/p>\n
<form><\/code> that needs to be protected from CSRF (which is pretty much<\/em> every
<form><\/code>).<\/p>\n
wp_nonce_field<\/code> is a convenience function to make our life easier building HTML forms:<\/p>\n
<form>\r\n<!-- Other form fields go here -->\r\n<?php wp_nonce_field( 'your_action_name' ); ?>\r\n<\/form><\/code><\/pre>\n
Function: wp_nonce_url<\/h4>\n
wp_nonce_url<\/code> where we need to validate that a user intended to click a link:<\/p>\n
<?php\r\n $action_url = wp_nonce_url( '\/change-color\/?color=blue', 'change_color' );\r\n \/\/ $action_url is now \"\/change-color\/?color=blue&_wpnonce=GENERATED_VALUE\"\r\n?>\r\n<a href=\"<?php echo esc_url( $action_url ); ?>\">Change to blue<\/a><\/code><\/pre>\n
Function: wp_create_nonce<\/h4>\n
wp_create_nonce<\/code> used to generate a nonce value for returning in JSON format (useful for returning in AJAX requests):<\/p>\n
<?php\r\n $nonce_value = wp_create_nonce( 'your_action_name' );\r\n\r\n return json_encode( array( 'nonce' => $nonce_value ) );\r\n?><\/code><\/pre>\n
Function: check_ajax_referer<\/h4>\n