Securing your WordPress plugin AJAX calls using nonces

AJAX Logo In my last article on this topic I wrote about proper way of implementing AJAX with jQuery in your WordPress plugins. In this article I will show you how to implementing AJAX with jQuery in your WordPress plugins in more secure way by using WordPress nonces. Cryptographic nonce is number passed during communication whose purpose is to prevent someone sniffing authenticated communication to commit replay attack

by replaying captured communication request. Nonces protect you because every request has its own unique nonce, this way both sides can detect replay attack attempts. Now lets take a look at how WordPress implements nonce security.

Keep in mind that WordPress nonce is a bit different than standard cryptographic nonce. WordPress nonce is not used only once, instead it remains valid for 12 to 24 hours after it has been generated. This decreases both your level of protection and performance impact of generating new nonce on every request. But we should use them when ever possible to make our code as close to bulletproof as possible.

So here's our PHP code from my Proper way of implementing AJAX with jQuery in your WordPress plugins article modified to include nonce security. First the part where we generate nonce and pass it to Javascript:

1
2
3
4
5
6
7
8
9
10
11
12
function my_plugin_script() {
    wp_enqueue_script('jquery');
    wp_enqueue_script('my_plugin_script', (WP_PLUGIN_URL.'/my_plugin/js/my_plugin_script.js'), array('jquery'), false, true);
    wp_localize_script('my_plugin_script', 'my_plugin_script_vars', 
        array(
            'ajaxurl' =>  admin_url('admin-ajax.php'),
            'my_plugin_my_action_nonce'=> wp_create_nonce('my_plugin_my_action')
            )
	);
}
add_action('wp_enqueue_scripts', 'my_plugin_script');
add_action('admin_enqueue_scripts', 'my_plugin_script');

In the line 7 of the preceding code block we are using wp_create_nonce() to generate nonce. We must remember both AJAX action 'my_plugin_my_action' and AJAX action nonce 'my_plugin_my_action_nonce' slugs because we will use them when we try to verify AJAX call supplied with this nonce. For convenience we will use same naming convention on our client side. Now here's the Javascript from my last article on this topic modified to use nonce security:

1
2
3
4
5
6
7
8
9
10
jQuery.post(my_plugin_script_vars.ajaxurl, {
    action: 'my_plugin_my_action',
    my_plugin_my_action_nonce: my_plugin_script_vars.my_plugin_my_action_nonce,
    js_data_for_php: 'Some Javascript data to pass to PHP AJAX handler'},
    function(php_data){
        my_plugin_script_vars.my_plugin_my_action_nonce = data.my_plugin_my_action_nonce;
        alert(php_data.result);
    },
    'json'
);

What is new are lines 3 and 6. Line 3 sends nonce received from PHP side during page load using wp_localize_script. This nonce will be verified by our AJAX action callback on PHP side. Line 6 is here because if nonce verification is successful we will receive new nonce along with AJAX data. This nonce will stay unchanged most of the time because WordPress nonces are valid for 12-24 hours after being generated but our AJAX calls will work even after nonce has changed because we update it each time our AJAX call returns. Now here's our PHP AJAX action callback modified to implement nonces:

1
2
3
4
5
6
7
8
9
10
11
12
13
function my_plugin_my_action_ajax_handler(){
    check_ajax_referer('my_plugin_my_action', 'my_plugin_my_action_nonce');
 
    $data_from_javascript = $_POST['js_data_for_php'];
    $data_for_javascript = 'Fetched from database or somewhere else using PHP';
    $response = json_encode(array('result' => $data_for_javascript));
 
    header('Content-Type: application/json');
    echo $response;
    exit;
}
add_action( 'wp_ajax_my_plugin_my_action', 'my_plugin_my_action_ajax_handler' );
add_action( 'wp_ajax_nopriv_my_plugin_my_action', 'my_plugin_my_action_ajax_handler' );

What's new is check_ajax_referer() call with our AJAX action slug and our AJAX action nonce slug. check_ajax_referer() will return -1 to our Javascript success callback to indicate that nonce verification was unsuccessful or process our data and return expected results if nonce verification was success.

Just one more thing. If you're planing to make your plugin compatible with PHP caching plugins like WP Super Cache or W3 Total cache you will not be able to reliably use nonces because nonces won't be generated on each page load.

Leave a Reply

Your email address will not be published. Required fields are marked *