0

My site sends different data to an external API under different circumstances. Luckily, most of it happens on form submissions, but there are a few instances where I need to make an API call from the client-side when a custom event fires.

$(document).on('someCustomEvent', function() {
  $.ajax({
    url: '/wp-admin/admin-ajax.php',
    method: 'post',
    data: {
      uid: $('#user-wrap').data('uid'),
      action: 'add_user_by_uid',
      security: addUserVars.nonce   // addUserVars{} comes from wp_localize_script()
    }
  });
});

Then in functions.php I have:

function add_user_by_uid() {
  check_ajax_referer('add_user_nonce', 'security');

  $basic_auth = 'Basic ' . base64_encode( PUBLIC_KEY . ':' . PRIVATE_KEY );
  $headers = array( 
    'Authorization' => $basic_auth,
    'Content-type' => 'application/json'
  );

  return = wp_remote_post( 'https://api.example.com/add/', array(
    'headers' => $headers,
    'body' => json_encode(array('uid' => sanitize_text_field($_POST['uid']))
    )
  );
  die();
}

How can I prevent someone from creating their own POST request and executing this API call from their own site (obviously at times other than when someCustomEvent fires)?

I've always felt like using a nonce protected against this, but I've never really given it that much thought since most of my experience using AJAX in WP has not been with updating users and other critical info with a 3rd party API like this is.... Anyway, it sounds like this measure isn't terribly hard to get around. Is there a mitigation that is more appropriate for a scenario like this?

4
  • You can't. you can only mitigate this without forcing a user to be logged in, and even then a user can just acquire a cookie or automate login. Although even with this, passing the uid directly to the remote API does not seem safe, especially with no sanitising
    – Tom J Nowell
    Nov 4 '20 at 14:56
  • This was just a watered down version of the actual code to illustrate my issue. So there's no check (that can't be spoofed) I can do in add_user_by_uid() to make sure the POST is coming from my site or anything like that?
    – Daveh0
    Nov 4 '20 at 15:03
  • HTTP is stateless, if they have a valid cookie and know the details for basic auth, they can make the request. You can check the referrer but referrers can be spoofed. You can add a nonce, but if the server has access to the site they can just read the nonce. The server could use tools such as Cypress or Behat to manually click through your sites user interface in a headless browser. If you're asking for a cast iron canonical answer as your Q reads literally, then that is not possible. Otherwise you're going to have to refine the Q to refer to a specific mitigation
    – Tom J Nowell
    Nov 4 '20 at 15:24
  • Question/code updated.
    – Daveh0
    Nov 4 '20 at 16:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.