How does check_ajax_referer() really work?
ajax, nonce, wordpress
Solution
Revising some AJAX procedures, I came to the same question. And it's a simple matter of checking the function code:
function check_ajax_referer( $action = -1, $query_arg = false, $die = true ) {
if ( $query_arg )
$nonce = $_REQUEST[$query_arg];
else
$nonce = isset($_REQUEST['_ajax_nonce']) ? $_REQUEST['_ajax_nonce'] : $_REQUEST['_wpnonce'];
$result = wp_verify_nonce( $nonce, $action );
if ( $die && false == $result ) {
if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
wp_die( -1 );
else
die( '-1' );
}
do_action('check_ajax_referer', $action, $result);
return $result;
}
If `wp_verify_nonce` is `false` and you haven't sent `false` in the `$die` parameter, then it will execute `wp_die( -1 );`.
In your sample code, `check_ajax_referer()` will break the execution and return `-1` to the AJAX call. If you want to handle the error yourself, add the parameter `$die` and do your stuff with `$do_check`:
$do_check = check_ajax_referer( 'myplg-nonce', 'nonce', false );
Note that the proper way to handle AJAX in WordPress is: register, enqueue and localize the JavaScript files using `wp_enqueue_scripts` instead of `wp_print_scripts`. See Use wp_enqueue_scripts() not wp_print_styles().
Check this update in December 2020 from a core contributor h/t: Rafael Atías:
Apparently we should now use `wp_add_inline_script` instead of `wp_localize_script` to expose a global object that needs to be used by your script.
Problem
Smart Wordpress people say that plugin developers should employ a nonce in each AJAX request that is sent from a page back to the wordpress blog (admin-ajax.php). This is done by (1) generating a nonce on the server side, via ``` $nonce = wp_create_nonce ('my-nonce'); ``` ...(2) making that nonce available to Javascript code that sends AJAX requests. For example you could do it like this: ``` function myplg_emit_scriptblock() { $nonce = wp_create_nonce('myplg-nonce'); echo "<script type='text/javascript'>\n" . " var WpPlgSettings = {\n" . " ajaxurl : '" . admin_url( 'admin-ajax.php' ) . "',\n" . " nonce : '" . $nonce . "'\n" . " };\n" . "</script>\n"; } add_action('wp_print_scripts','myplg_emit_scriptblock'); ``` ...and then (3) the javascript ajax logic references that global variable. ``` var url = WpPlgSettings.ajaxurl + "?action=my-wp-plg-action&" + "nonce=" + WpPlgSettings .nonce + "docid=" + id; $.ajax({type: "GET", url: url, headers : { "Accept" : 'application/json' }, dataType: "json", cache: false, error: function (xhr, textStatus, errorThrown) { ... }, success: function (data, textStatus, xhr) { ... } }); ``` ...and finally (4) checking the received nonce in the server-side logic. ``` add_action( 'wp_ajax_nopriv_skydrv-hotlink', 'myplg_handle_ajax_request' ); add_action( 'wp_ajax_skydrv-hotlink', 'myplg_handle_ajax_request' ); function myplg_handle_ajax_request() { check_ajax_referer( 'myplg-nonce', 'nonce' ); // <<=----- if (isset($_GET['docid'])) { $docid = $_GET['docid']; $response = myplg_generate_the_response($docid); header( "Content-Type: application/json" ); echo json_encode( $response ) ; } else { $response = array("error" => "you must specify a docid parameter."); echo json_encode( $response ) ; } exit; } ``` But how does the check really work?