Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Secure ajax GET/POST request for server

suppose I work with some kind of API and my file server.php handles the connection to the API service. on my client side I use AJAX call like this:

$http({
         url : 'server/server.php',
         method : 'GET',
         data : { getContent : true }
     });

in my server.php I handle it like this:

if(isset($_GET['getContent'])){
    $content = get_content();
}

function get_content(){...}

i just wonder what prevents any one send AJAX call with the same getContent parameter and get all my data? how can i secure it and make sure only calls from my application will get the relevant data back?

thank you!

like image 777
Dima Gimburg Avatar asked Dec 20 '22 04:12

Dima Gimburg


2 Answers

I guess you are concerned about CSRF attacks. Read more about this here: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29_Prevention_Cheat_Sheet

One of the mostly used option to secure your request will be: - Generate a token and send it with the request for a session. This token can be identified by your WebServer as originating from a specific client for a specific session

like image 62
FakirTrappedInCode Avatar answered Dec 24 '22 03:12

FakirTrappedInCode


2022 Update

This is a 7 year old post and the link in the link-only "accepted" answer is broken.

So, I'm going to offer a basic walkthrough and a complete model.

Remember, the $_SESSION will be preserved even in the AJAX handler, if it's all from the same domain. So, you can use that to check things.

Use $_POST

I presume you're using $_POST and not $_GET since you're concerned about security. If not, then much of this might not be important anyway.

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  $post_method = true;
}

Ensure the $_SERVER['HTTP_REFERER'] is from your own site

if ( (!empty($_SERVER['HTTP_REFERER']))
&&   ($_SERVER['HTTP_REFERER'] === "https://example.tld/my_sending_page.php") ) {
  $from_my_server = true;
} 

If you're not sure what this should be, run a test on your own server to see what this should be:

echo $_SERVER['HTTP_REFERER'];

Verify XMLHTTP/AJAX request via $_SERVER array

if ( (!empty($_SERVER['HTTP_X_REQUESTED_WITH']))
&&   ( strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') ) { 
  $ajax = true; 
} else { 
  $ajax = false; 
} 

Use a token

This is the hard part, but not too hard.

  1. Create the token
  2. Set the token in $_SESSION
  3. Put the token in the AJAX header
  4. AJAX responder: confirm the AJAX header token with the $_SESSION token

send_from_me.php

// Create the token
//$token = md5(rand(10000,99999));  // Not recommended, but possible
$token = bin2hex(random_bytes(64));

// Store in SESSION
$_SESSION["token"] = $token;
// Assuming your AJAX is this
const AJAX = new XMLHttpRequest();

// This goes inside your AJAX function somewhere before AJAX.send
//
AJAX.setRequestHeader("ajax-token", "<?php echo $_SESSION["token"]; ?>");
//
// That creates $_SERVER['HTTP_AJAX_TOKEN'] which we can use later

ajax_responder.php

session_start(); // Must have

if ($_SERVER['HTTP_AJAX_TOKEN'] === $_SESSION["token"]) {
  $token_match = true;
} else {
  echo "No script kiddies!";
  exit();
}

// Now it's safe for your AJAX responder to proceed

Let's put all of this into a working example

sending_from.php

<?php

session_start();

$token = bin2hex(random_bytes(64));
$_SESSION["token"] = $token;

?>

<!DOCTYPE html>
<html>
  <head>
    <title>My AJAX Sender</title>
  </head>
  <body>

    <script>
      function ajaxFormData(formID, postTo, ajaxUpdate) {

        // Bind a new event listener every time the <form> is changed:
        const FORM = document.getElementById(formID); // <form> by ID
        const FD = new FormData(FORM); // Bind to-send data to form element
        const AJAX = new XMLHttpRequest(); // AJAX handler

        // This runs when AJAX responds
        AJAX.addEventListener( "load", function(event) {
          document.getElementById(ajaxUpdate).innerHTML = event.target.responseText;
        } );

        // This runs if AJAX fails
        AJAX.addEventListener( "error", function(event) {
          document.getElementById(ajaxUpdate).innerHTML =  'Oops! Something went wrong.';
        } );

        // Add your token header
        AJAX.setRequestHeader("ajax-token", "<?php echo $_SESSION["token"]; ?>");

        // Open the POST connection
        AJAX.open("POST", postTo);

        // Data sent is from the form
        AJAX.send(FD);

      }
    </script>

    <div id="ajax_changes">Replace me with AJAX</div>

      <form id="ajaxForm">
        <input type="text" name="the_simple_response">
        <button type="button" onclick="ajaxFormData('ajaxForm', 'ajax_responder.php', 'ajax_changes');">Send my Secure AJAX</button>
      </form>

  </body>
</html>

ajaxcheck.inc.php

<?php

$mysite = 'https://example.tld';

// All in one test
if (($_SERVER['REQUEST_METHOD'] == 'POST')
&& ((!empty($_SERVER['HTTP_REFERER'])) && ($_SERVER['HTTP_REFERER'] === "$mysite/my_sending_page.php"))
&& ((!empty($_SERVER['HTTP_X_REQUESTED_WITH'])) && ( strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'))
&& ($_SERVER['HTTP_AJAX_TOKEN'] === $_SESSION["token"])) {
  
  $ajax_legit = true;
  
} else {
  
  echo "No script kiddies!";
  exit();
  
}

?>

ajax_responder.php

<?php

session_start();

// Do all that checking we're learning about by neatly including the file above
require_once('ajaxcheck.inc.php');

// Process your AJAX
echo $_POST['the_simple_response'];

?>
like image 34
Jesse Steele Avatar answered Dec 24 '22 02:12

Jesse Steele