{"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

Nonces and Cross-Site Request Forgery<\/h3>\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

<?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

On the back end, you handle the picture form submissions:<\/p>\n

if ( is_user_logged_in() && isset( $_REQUEST['picture_url'] ) ) {\r\n  \/\/ Save the picture here\r\n}<\/code><\/pre>\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

http:\/\/your-site.com\/submit-picture\/?picture_url=fake-picture.jpg<\/code><\/pre>\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

How to Prevent CSRF<\/h3>\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

    \n
  1. A nonce is generated.<\/li>\n
  2. That nonce is submitted with the form.<\/li>\n
  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!<\/li>\n<\/ol>\n

    Let’s Add a Nonce<\/h3>\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

    <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

    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

    \/\/ 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

    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<\/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

    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

    http:\/\/your-site.com\/submit-picture\/?picture_url=fake-picture.jpg&_wpnonce=NONCEVALUE<\/code><\/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

    Try These Nonce Functions<\/h3>\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

    Function: wp_verify_nonce<\/h4>\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 wp_verify_nonce<\/code><\/p>\n

    Just like the examples above, use 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

    Codex entry for wp_verify_nonce<\/a><\/p>\n

    Function: wp_nonce_field<\/h4>\n

    What it does:<\/strong> Prints a hidden <input><\/code> tag containing a nonce value.<\/p>\n

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

    Just like in the examples above, 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

    Codex entry for wp_nonce_field<\/a><\/p>\n

    Function: wp_nonce_url<\/h4>\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 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

    Codex entry for wp_nonce_url<\/a><\/p>\n

    Function: wp_create_nonce<\/h4>\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 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

    Codex entry for wp_create_nonce<\/a><\/p>\n

    Function: check_ajax_referer<\/h4>\n

    What it does:<\/strong> Checks for a submitted nonce using wp_verify_nonce<\/code>, and if the validation fails, stops execution.<\/p>\n

    check_ajax_referer<\/code> is another convenience function mainly for use in AJAX requests. You can use it to skip doing the wp_verify_nonce<\/code> check yourself:<\/p>\n

    <?php\r\n  \/\/ Forged requests won't get past this line:\r\n  check_ajax_referer( 'your_action_name' );\r\n\r\n  \/\/ Do your action here\r\n?><\/code><\/pre>\n

    Codex entry for check_ajax_referer<\/a><\/p>\n

    Nonces for Everything!<\/h3>\n

    CSRF vulnerabilities range from harmless (e.g. poll submissions) to extremely dangerous<\/strong> (e.g. modifying passwords or permissions).<\/p>\n

    Regardless of severity, you should be using nonces to prevent CSRF any time a user does an action<\/strong> 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. <\/p>\n

    Use nonces, stop forgery, and foil hackers!<\/p>\n","protected":false},"excerpt":{"rendered":"

    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).<\/p>\n","protected":false},"author":223334,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"c2c_always_allow_admin_comments":false,"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[4],"tags":[],"jetpack_publicize_connections":[],"acf":[],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":197334,"url":"https:\/\/css-tricks.com\/introduction-to-wordpress-front-end-security-escaping-the-things\/","url_meta":{"origin":197463,"position":0},"title":"Introduction to WordPress Front End Security: Escaping the Things","date":"March 23, 2015","format":false,"excerpt":"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\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":7886,"url":"https:\/\/css-tricks.com\/what-is-xss\/","url_meta":{"origin":197463,"position":1},"title":"What is Cross Site Scripting or XSS?","date":"November 19, 2010","format":false,"excerpt":"I think the name \"cross site\" is confusing. It's easy to hear that and think it involves code on one website attacking code on another website. That's not what it is. Not to mention its unfortunate \"true\" acronym. It simply means: executing abritrary JavaScript code on the page. This could\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":20203,"url":"https:\/\/css-tricks.com\/w3conf-brad-hill-html5-security-realities\/","url_meta":{"origin":197463,"position":2},"title":"[W3Conf] Brad Hill: “HTML5 Security Realities”","date":"February 22, 2013","format":false,"excerpt":"Brad Hill (@hillbrad) works at PayPal work works with the W3C on security issues. These are my notes from his presentation at W3Conf in San Francisco as part of this live blog series. You can't read anything about security without huge hyperbole about HTML security. Is it correct? Brad says\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":167531,"url":"https:\/\/css-tricks.com\/oembed\/","url_meta":{"origin":197463,"position":3},"title":"oEmbed Bring Embedded Pens All Over","date":"May 9, 2014","format":false,"excerpt":"oEmbed is a neat little technology that allows for rich content to be embedded into other content very easily. You paste a link to the \"thing\" and, when published, that link magically transforms into something much more useful than a link. A quintessential example is a link to a YouTube\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":360346,"url":"https:\/\/css-tricks.com\/using-svg-in-wordpress\/","url_meta":{"origin":197463,"position":4},"title":"Using SVG in WordPress (2 Helpful Plugin Recommendations)","date":"January 21, 2022","format":false,"excerpt":"SVG is a great image format, so it's nice to able to use it in WordPress. If you're looking to be using SVG in WordPress. , we've got you covered here with all the best options.","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2022\/01\/using-svg-in-wordpress.png?fit=1200%2C966&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":293636,"url":"https:\/\/css-tricks.com\/weekly-platform-news-preventing-image-loads-with-the-picture-element-the-web-we-want-svg-styles-are-not-scoped\/","url_meta":{"origin":197463,"position":5},"title":"Weekly Platform News: Preventing Image Loads with the Picture Element, the Web We Want, Svg Styles Are Not Scoped","date":"August 1, 2019","format":false,"excerpt":"In this week's week roundup of browser news, a trick for loading images conditionally using the picture element, your chance to tell bowser vendors about the web you want, and the styles applied to inline SVG elements are, well, not scoped only to that SVG. Let's turn to the headlines...\u2026","rel":"","context":"In "Weekly Platform News"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2019\/08\/web-platform-news-190801.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]}],"featured_media_src_url":null,"_links":{"self":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/197463"}],"collection":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/users\/223334"}],"replies":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/comments?post=197463"}],"version-history":[{"count":33,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/197463\/revisions"}],"predecessor-version":[{"id":375268,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/197463\/revisions\/375268"}],"wp:attachment":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media?parent=197463"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/categories?post=197463"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/tags?post=197463"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}