When you integrate AJAX technology in your web application, you are exposed to security problem on call requests. Here is a simple solution to secure your calls...

Context:

To build your AJAX request, you should create a Javascript function which call a HTTP ressource in your web application.

Take for instance a request that modify a plugin activation through the url http://www.myapp.ext/ajax/myplugin.

The main security problem is when anyone know the url endpoint, this person can modify the plugin activation state. This is a security breach in your web application.

Idea is to implement a functionality which identify only the person, connected to your web application, who request the state modification.

Analysis

There are some methods to secure AJAX calls and I give you a simple solution.

This one use php session where a random key is generated and saved in session, then used in AJAX request to validate call request.

The new url endpoint is http://www.myapp.ext/ajax/myplugin?skey=<Random key>.

Implementation

1. Server side: Generate random key

The aim is to generate a random key and save it in session then use it during the build of javascript function.

Using Symfony implementation

// AcmeBundle/Controller/PluginController.php

// Controller Action
public function indexAction(Request $request)
{
  $session = $request->getSession();
  if (!($session->isStarted())) {
    $session->start();
  }
  $server_key = $session->get('skey');
  if(empty($server_key)){
    $server_key = $this->randomString();
    $session->set('skey', $server_key);
  }

  return $this->render('AcmeBundle:Plugin:index.html.twig', array(
    'server_key'  => $server_key  // Pass random key to twig template
  ));
}

// Random string generator
private function randomString($length = 10){
  $characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  $charactersLength = strlen($characters);
  $randomString = "";
  for ($i = 0; $i < $length; $i++) {
    $randomString .= $characters[rand(0, $charactersLength - 1)];
  }
  return $randomString;
}

Using PHP implementation


session_start();

// Random string generator
function randomString($length = 10){
  $characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  $charactersLength = strlen($characters);
  $randomString = "";
  for ($i = 0; $i < $length; $i++) {
    $randomString .= $characters[rand(0, $charactersLength - 1)];
  }
  return $randomString;
}

if(!isset($_SESSION['skey'])){
  $_SESSION['skey'] = randomString();
}

2. Client side: Build javascript function

A POST request is created with parameters active which enable the plugin.


$.ajax({
  url: "http://www.myapp.ext/ajax/myplugin?skey=<Server key>",
  method: 'POST',
  data: {active: <Activation state> }
}).done(function () {
  //<Action when the request successes>
});

where:

  • <Server key>: The server key => Use {{ server_key }} using symfony implementation in Twig template or <?php echo $_SESSION['skey']; ?> using PHP implementation.
  • <Activation state>: The activation state (0 or 1).
  • <Action when the request successes>: Action to do when the request successes.

3. Server side: Treatment of request

When the HTTP resource is called, a check with the server key is done.

Using Symfony implementation


// AcmeBundle/Controller/PluginController.php

// Controller Action
public function activationAction(Request $request)
{
  if($request->isMethod('POST')) {
    $session = $request->getSession();

    if (!($session->isStarted())) {
      $session->start();
    }

    $server_key = $session->get('skey');
    $client_key = $request->query->get('skey');

    // >>>> Security check
    if(empty($server_key) || empty($client_key) || ($server_key != $client_key)) {
      return new Response("Unauthorized", Response::HTTP_UNAUTHORIZED);
    }

    //Treatment to do to (de)activate plugin

    return new Response("Accepted", Response::HTTP_ACCEPTED);
  }

  return new Response("Bad Request", Response::HTTP_BAD_REQUEST);
}

Using PHP implementation


session_start();

// >>>> Security check
if(empty($_SESSION['skey']) || empty($_POST['skey']) || ($_SESSION['skey'] != $_POST['skey'])) {

  //Treatment when a bad user do the request

} else {

  //Treatment to do to (de)activate plugin

}

Conclusion

Here is a simple solution to secure your AJAX calls against unexpected request.

You can adapt this strategy to other technologies if you want.

Other solutions exist (more complex and more safe) but this one is sufficient against main attacks.

See you in the next article... xD

Article précédent Article suivant


Ajouter un commentaire