WordPress Ajax Data Security

I am aware of nonces to check for intentions but can’t make any sense if I can use them to restrict for database reads somehow? I mean, I am fetching records from the database using AJAX but I don’t want anyone to have that data by some other means (POST requests to that admin-ajax.php page).

For example, in my AJAX handler I have this much of work (I am sending $myid along with the action in the AJAX call):

Read More
$myid = 5575;
$query = "SELECT `id` FROM table WHERE `user_id` = $myid;";
$data = $wpdb->get_col( $query );
echo json_encode($data);
die();

I am calling this much of data through AJAX, how can I protect it from evil users who can try to make direct POST calls to that page?

Edit: @Bainternet Thanks for the detailed explanation, appreciate that but my question is really about what @Milo said and I can see that unless I can create a unique nonce for that request, I can’t do anything as per @goldenapples. Right?

I am handling all this stuff with Facebook JS SDK, so everything that has to be done is with AJAX only (without page load), and whatever I can think of making the user sign in wp_signon() or anything, anyone else can do that too without authority. So, I don’t have any uniqueness in a user who is actually using the site and the one who is trying to get read some data out other than the site makes the call to fetch the data. And whatever I can do to make it login or anything, that can be replicated by someone else too. Can anyone think of something?

Update: I think I have found a workaround by just calling it with action and get other parameters from Facebook using PHP SDK. But I am still open for suggestions or anything else for that matter.

Related posts

Leave a Reply

3 comments

  1. There are a few things you can do to make more secure:

    First the Ajax call it self should be made with a WordPress nonce like you said:

    <script type="text/javascript" >
        jQuery(document).ready(function($) {
            var data = {
                action: 'ACTION_NAME',
                Whatever_data: 1234,
                _ajax_nonce: <?php echo wp_create_nonce( 'my_ajax_nonce' ); ?>
            };
            $.post(ajaxurl, data, function(response) {
                alert('Got this from the server: ' + response);
            });
        });
    </script>
    

    In the above code mind the two attributes action and _ajax_nonce which both are needed to verify the call to admin-ajax.php, in the first few lines of code it check if an action was sent to the server and if not then it die()‘s (FIRST CHECK) then using that action you with an action hook you call your own ajax function:

    //if you want only logged in users to access this function use this hook
    add_action('wp_ajax_ACTION_NAME', 'my_AJAX_processing_function');
    
    //if you want none logged in users to access this function use this hook
    add_action('wp_ajax_nopriv_ACTION_NAME', 'my_AJAX_processing_function');
    

    and if you want both logged in users and none logged in visitors to access this function the use both hooks (SECOND CHECK).

    then in you ajax function the first thing you should do is check for the ajax referrer (THIRD CHECK):

    function my_AJAX_processing_function(){
       check_ajax_referer('my_ajax_nonce');
       //do stuff here
    }
    

    next when running queries on database with user input you should use $wpdb->prepare for escaping and validation so instead of:

    $query = "SELECT `id` FROM table WHERE `user_id` = $myid;";
    $data = $wpdb->get_col( $query )
    

    use:

    $data = $wpdb->query( $wpdb->prepare("SELECT `id` FROM table WHERE `user_id` = %d",$myid));
    
  2. I am not entirely sure how secure this is, but WordPress Ajax handlers are user-aware (I guess request is still request so cookies are checked and if user is logged in that is accounted for). So you can make usual current_user_can() check in them.

  3. presumably one is logged in when making this request? you can hash some data and create a unique per-user nonce on your page, pass it with your ajax request, and check it against the logged in user. or maybe I’m misunderstanding what you’re doing here.