Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send secure AJAX requests with PHP and jQuery

The problem

So for a while now I've been experimenting with different AJAX approaches in sending data to a server that will be processed and stored inside a MySQL database.

The page that the AJAX request hits api.php, uses PHP's PDO prepared statements to save the data, so MySQL injections aren't really a problem and the passwords or data that needs to be encrypted are also handled by api.php which isn't what I'm asking here. My question relates more to how to ensure the data is secure when being transferred from the client to the server.

The approaches

I currently have (for the login example I have included below):

  • SSL Cert/HTTPS running on the domain.
  • Certain AJAX request (obviously not this login request example as there is no session to begin with) will only work if the PHP Session is valid across the site (used on both login.php and api.php in this example).
  • Rate limiting on api.php when accessing functions.
  • PHP PDO prepared statements when interacting with the database inside api.php.
  • Encrypts sensitive data inside api.php (not relevant to the question).

The questions

Finally, my questions are:

  1. Is this approach to using asynchronous HTTP (Ajax) requests safe enough to use rather than just submitting data to a PHP page and redirecting onwards? (As this way improves the user's experience).
  2. How can I check to know that the data my user's are sending hasn't been tampered with?
  3. Am I reasonably doing enough to protect my user's data, if not, what else can I do?

The example

I understand everyone has different approaches to handling their site's data and transporting that data. I also understand that no matter what you do, you can never be 100% protected, as there may be vulnerabilities and ways around your system that you can't account for. I'm looking for feedback/improvements on my general approach in sending data securely rather than criticism of the specific code below as it is only an example. But any constructive answers are welcome. Thanks for taking the time to read/answer.

function loginUser() {     var process = "loginUser";     var data = $("form").serializeArray();     data[1].value = SHA512(data[1].value); // sha then encrypt on api.php page      data = JSON.stringify(data);      $("#loginButton").html('<i class="fa fa-spinner fa-pulse fa-lg fa-fw"></i> Login');     $.ajax({         type: "POST",         url: "api.php",         data: {"process": process, "data": data},         success: function(data) {             if (data.response.state == "success") {                 // if api.php returns success, redirect to homepage             } else {                 // if api.php returns failure, display error             }           },         error: function(jqXHR, textStatus, errorThrown, data) {             // error handling         },         dataType: "json"     }); } 
like image 854
Luke Brown Avatar asked Jun 19 '16 23:06

Luke Brown


People also ask

How can I make AJAX request secure?

AJAX calls are itself protect CSRF using “Common Origin Policy” when CORS is disabled and JSONP requests are blocked. To prevent CSRF attack one step ahead, we can implement Anti Forgery token similar to MVC framework. AJAX calls can be called from web application as well as from MVC. In MVC, @html.

Can I use AJAX with jQuery?

jQuery provides several methods for AJAX functionality. With the jQuery AJAX methods, you can request text, HTML, XML, or JSON from a remote server using both HTTP Get and HTTP Post - And you can load the external data directly into the selected HTML elements of your web page!

Can we use PHP in AJAX?

Start Using AJAX Today In our PHP tutorial, we will demonstrate how AJAX can update parts of a web page, without reloading the whole page. The server script will be written in PHP. If you want to learn more about AJAX, visit our AJAX tutorial.

Can AJAX be hacked?

If someone has complete access to a browser, then they can run any code they like in it - including modifying or adding JavaScript to your pages. That has absolutely nothing to do with a site using Ajax though — any point where the client interacts with the server may be vulnerable.


2 Answers

1. Check the ORIGIN header

As specified by OWASP, this is not enough but recommended :

Although it is trivial to spoof any header from your own browser, it is generally impossible to do so in a CSRF attack, except via an XSS vulnerability. That's why checking headers is a reasonable first step in your CSRF defense, but since they aren't always present, its generally not considered a sufficient defense on its own.

And by Mozilla :

The Origin header is considered helpful against JSON data theft and CSRF attacks. The information provided by Origin--a bit of contextual request-creation information--should provide hints to web servers about trustworthiness of requests [...]

Checking the HTTP_ORIGIN header could be written as :

header('Content-Type: application/json');  if (isset($_SERVER['HTTP_ORIGIN'])) {     $address = 'http://' . $_SERVER['SERVER_NAME'];     if (strpos($address, $_SERVER['HTTP_ORIGIN']) !== 0) {         exit(json_encode([             'error' => 'Invalid Origin header: ' . $_SERVER['HTTP_ORIGIN']         ]));     } } else {     exit(json_encode(['error' => 'No Origin header'])); } 

1. (bis) Check the REFERER header

Again from OWASP :

If the Origin header is not present, verify the hostname in the Referer header matches the site's origin. Checking the referer is a commonly used method of preventing CSRF on embedded network devices because it does not require a per-user state.. This method of CSRF mitigation is also commonly used with unauthenticated requests [...]

Checking the HTTP_REFERER is also quite simple in PHP with $_SERVER['HTTP_REFERER'], you can just update the above code with it.


BE CAREFUL with the checking which always need to be really specific : do no check just example.com or api.example.com but the full https://example.com. Why ? Because you could spoof this check with an origin like api.example.com.hacker.com.


2. Generate CSRF tokens

A well-explained answer specific to PHP has been given there, in short :

  1. Generate the token :

    session_start(); if (empty($_SESSION['csrf_token'])) {     $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } 
  2. Add it in your generated views via a meta (like Github) :

    <meta name="csrf-token" content="<?= $_SESSION['csrf_token'] ?>"> 
  3. Setup jQuery ajax calls to include this token :

    $.ajaxSetup({     headers : {         'CsrfToken': $('meta[name="csrf-token"]').attr('content')     } }); 
  4. Server-side check your AJAX requests :

    session_start(); if (empty($_SESSION['csrf_token'])) {     $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); }  header('Content-Type: application/json');  $headers = apache_request_headers(); if (isset($headers['CsrfToken'])) {     if ($headers['CsrfToken'] !== $_SESSION['csrf_token']) {         exit(json_encode(['error' => 'Wrong CSRF token.']));     } } else {     exit(json_encode(['error' => 'No CSRF token.'])); } 

Most PHP frameworks have their own CSRF implementation, which more or less lay upon the same principle.


3. Sanitize validate user input.

You always must filter espace inputs and validate them.


4. Protect your server

  • Limit the number of your requests.
  • Use https as much as possible.
  • Block bad queries.
  • Protect POST requests.

5. Never trust user input

As @blue112 said, it is one of the most elementary security principles.

enter image description here

like image 105
Ivan Gabriele Avatar answered Sep 18 '22 08:09

Ivan Gabriele


Short answer: you can't protect your client side.

Long answer:

  • Using AJAX is just as safe as posting data with, for instance, a form.
  • Using HTTPS prevents man-in-the-middle to see your user data, so the actual data sent by the user are safe.

You can't do anything to make the browser prove that it's actually your javascript code that runs on the client side. Then, the obvious action to take is the most simple one: NEVER TRUST USER INPUT.

This mean, as you started to do, securing your server side using session, rate limiting, data validation, fail2ban (banning a client ip after a certain number of fail), log monitoring...

like image 27
blue112 Avatar answered Sep 20 '22 08:09

blue112