How to protect an AJAX request?

Asked

Viewed 3,142 times

16

I would like to know how to protect these requests, so that they do not work if the user tries to use them improperly...

An example is a system that updates the database every 5 minutes, but the user goes to the console and uses the resend to do as many times as he wants... in which case he should be denied access.

What safe ways to protect these requests AJAX?

  • 2

    The way I think about it is you have sessions and a counter on the server, and you have it on the browser. You’ve tried this before?

  • 3

    If the system is well developed it will not be necessary to protect ajax, it should have one or two validations in the backend, and ajax should send and take data to layer before validation and not directly in the persistence layer.

  • 6

    To protect against automated data entry systems etc just make a token Hidden field with time-based value and encryption, one of the backend validations will be if the token is equal to the input.

  • 2

    I also think about using Tokens. The application itself returns the token that should "exist" in the Ajax request. If it does not exist, the operation is canceled.

  • Just pointing out Q&A != forum

  • Jackson, has a way to modify ajax of your site, is expensive service. Tip use javascript prototype to do this. But why not use PHP? forgets javascript. If you try to make API?

  • 4

    You need to explain what problems you want to deal with, because each case is a case. In principle, the simple answer is "no ajax is possible". But in some cases, you can avoid repetition using nonces (a kind of non-reusable token) controlled by the server (as is done in most of the replay attack lock). The fact is that virtually everything the user can do for the browser can be emulated by a malicious application in the slightest detail. Only captcha and the like to prevent automated attacks, but then impacts usability (and does not prevent "manual attacks").

Show 2 more comments

4 answers

12


What are the safest ways to protect these requests?

The safest way to protect the data is using SSL certificate which may be self-sign, in the case of intranet, or issued by certification company. With paid certificates, I have good experiences with the Godaddy related to the SSL certificate, but also has the Rapidssl that I haven’t used yet, but the price is very inviting.

To avoid bots indication of use Captchas and/or limit the number of requests to every X seconds. If you want to implement, recommend the reCAPTCHA google.

Also perform treatments on stored data to avoid XSS (Cross-site scripting), so that it is not possible to use HTML tags in the data.

Block external access to your application

You can make Apache block external access. You can also use CSRF (Cross-site request forgery) inside your/your applications to tell which requests are really reliable, which is usually inserted via an Hidden input with a token, and thus avoid a type of bot attack.

Using Session to avoid attacks is a good idea?

This idea may work in forms where the user needs to be logged in, but it is not safe. Why do I say this? If you have already developed an application by creating Httprequest and Httpresponse, for example, you must have already killed the riddle.

What happens is that Sesssions and cookies are "optional" stores (you can disable this in your browser, for example) and are easily forged by a malicious person, IE, can not be controlled forever, besides it may be falsely active.

Note: when I say falsely active it is because the request can fulfill all the normal requirements, informing the server that it is active, but at each request it is renewed causing Sesssion to accept again as if it were the first.

The sad story of the server that accepts everything the client says

It would be more or less like this, taking into account that request 1 was sent at 13:01:

  • [13:01:01]192.168.1.1 - Send a request to the server.
  • [13:01:01]Server question: How many times have you, 192.168.1.1, tried to send this request? Answer for the header through the Session.
  • [13:01:02]192.168.1.1 responds: Once only.
  • [13:01:03] Server receives and responds: Ok, so I accept your Session and consequently your #Handshake request
  • //But the 192.168.1.1 is a bot and after the Handshake (handshake) it will do this several times saying whenever it’s the first time and your server will believe it
  • [13:01:03]192.168.1.1 - Send another request to the server.
  • [13:01:04]How many times have you, 192.168.1.1, tried to submit this request? Reply by header through Sesssion.
  • [13:01:05]192.168.1.1 responds: Once only. D
  • [13:01:06] Server receives and responds: Ok, so I accept your Session and consequently your #Handshake request
  • (And so it can go on for days, weeks, months or until the server goes down)

Well, I hope you’ve made it clear why you’re not using that. I recommend you store the ips and attempts on your server, don’t let the bad guy tell you he’s innocent, or let him tell you, but make sure the information matches the reality.

But doesn’t that burden my server? It costs a bit, but allows you to make a future ip lock, find out who is trying to attack, and do not lose this information easily.

Note: I cannot have paid digital certificate in my intranet?

According to the Godaddy and the Rapidssl, it is no longer possible to issue SSL certificates for use on intranet, since November 1, 2015, the IP must be listed on Whois. Any questions, visit this link who has the observation.

Completion

I hope I’ve helped you and want to make it clear that there are other ways to attack your form, but implementing these issues will make your forms a little safer. Remembering that there is no guarantee of 100% security, so always be looking for ways to attack your system. There are also some interesting tools for testing web applications.

Hug and good luck in developing your applications!

Links that can help you

7

There is a simple solution that is set through the Apache server, for this you need to activate the Cross-Site XMLHttpRequests in the mod_headers:

Read more here on how to activate Cors

What the CORS does?

It blocks requests that are made by other domains. But in order to control your outgoing requests, you will now need to send the header release via your PHP file, a simple solution to control:

Using a class that enables XSS (Cross-Site Scripting):

class XSSSecurityAccessOrigin
{

    private $permission_domains = array();
    private $request;
    private $secureProtocol = false;
    private $allowAll = false;

    public function __construct($request, $secureProtocol = false, $allowAll = false)
    {
      $this->permission_domains = array(
       'www.seusite.com.br',
       'www.subdominio.seusite.com.br' 
      );
      $this->request = $request;
      $this->secureProtocol = $secureProtocol;
      $this->allowAll = $allowAll;
    }

    public function accessControlAllowOrigin()
    {
        $security = '';
        if (in_array($this->request, $this->permission_domains)) {  
           if ($this->secureProtocol) {
               $security = 's';
           }
            $this->request = 'http{$security}://' . $this->request;
            return true;
        } else {
        return false;
        }
    }

    public function getJSON()
    {
        if ($this->allowAll) {
           $this->request = '*';
        }
        header("Access-Control-Allow-Origin: {$this->request}");
    }

}  
//como usar a classe
$permission = new XSSSecurityAccessOrigin($_SERVER['SERVER_NAME']);

if ($permission->accessControlAllowOrigin()) {
   $permission->getJSON();

   $data = $POST['request'];

   echo json_encode(array('success'=>true, 'data'=>$data));
}

This security will ensure that your requests are made only by the owner, or by the domains that will be allowed access.

Also, to ensure even more security, you can establish a token type validation CSRF (Cross Site Request Forgery), This allows you to check if the request came from the same location.

Zend Framework, in this case is version 1.12, has a plugin that allows you to do this, follow the example:

<?php
class Application_Plugin_Csrf extends Zend_Controller_Plugin_Abstract {
    public function preDispatch(Zend_Controller_Request_Abstract $request) {
        $csrf = new Zend_Session_Namespace('csrf-token');
        if ($request->getMethod() == 'POST' && !$this->isWhiteListed($request)) {
            if (!isset($_POST['csrf-token']) || !in_array($_POST['csrf-token'], $csrf->list)) {

                $writer = new Zend_Log_Writer_Stream(APPLICATION_PATH.'/data/error.log');
                $logger = new Zend_Log($writer);

                $logger->log('Invalid CSRF: expected '.$csrf->value.' and received '.$_POST['csrf-token'], Zend_Log::ERR);

                $this->getResponse()
                    ->clearHeaders()
                    ->setHttpResponseCode(403)
                    ->setRedirect('/error')
                    ->sendResponse();
            }
        }
    }

    private function isWhiteListed($request) {
        $actions = array(
          'error',
          'url-liberada-x',
          'url-liberada-y'
        );
        if (in_array($request->getActionName(), $actions)) {
            return true;
        }

        return false;
    }
} 

5

I don’t know much when it comes to ajax, I prefer to carry out the validations in the back-end, I leave to the front-end the pure role of interface, the rest, as validations prefer in the server-side.

I will respond the way I usually do with my requests, not just when it comes to being ajax.

I use the token, one token is generated when the page is called, and added to a input Hidden type, I send it with ajax together with the other data, already in the php me valid that token several times to see if the token is valid.

I set a small quick example to see that even having knowledge of the system is not possible to hack it, follows the description of each file:

index php.: responsible for displaying the person registration form, the list of registered persons, in it I present a token at the beginning of the page, and assign it in a field of type Hidden, there is also the ajax responsible for sending the data.

<?php
include 'Pessoa.php';
$_SESSION['token'] = crypt("MyAppIDKey" . time() . "MyTokenSecurity");
?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Segurança Ajax</title>
        <script  src="jquery.min.js"></script>
    </head>

    <body>
        <form class="myForm" method="get">
            <p>
                <label>Nome: </label>
                <input type="text" name="nome" />
            </p>

            <p>
                <label>Idade: </label>
                <input type="number" name="idade" />
            </p>

            <p>
                <label>E-mail: </label>
                <input type="email" name="email" />
            </p>

            <p>
                <input type="hidden" name="token" value="<?php echo $_SESSION['token']; ?>" />
                <input type="submit" value="Enviar" />
                <input type="reset" value="Limpar" />
            </p>
        </form>

        <div class="result"></div>

        <?php if(isset($_SESSION['pessoas']) and !empty($_SESSION['pessoas'])): ?>
        <table>
            <thead>
                <tr>
                    <th>Nome</th>
                    <th>Idade</th>
                    <th>Email</th>
                </tr>
            </thead>

            <tbody>
                <?php foreach ($_SESSION['pessoas'] as $pessoa): ?>
                    <tr>
                        <td><?php echo $pessoa->getNome(); ?></td>
                        <td><?php echo $pessoa->getIdade(); ?></td>
                        <td><?php echo $pessoa->getEmail(); ?></td>
                    </tr>
                <?php endforeach; ?>
            </tbody>
        </table>
        <?php endif;?>


        <script>
        $(function()
        {
            $("form.myForm").submit(function()
            {
                $.ajax(
                {
                    type: "GET",
                    url: "callMyController.php",
                    data: 
                    {
                        nome: $("input[name = nome]").val(),
                        idade: $("input[name = idade]").val(),
                        email: $("input[name = email]").val(),
                        token: $("input[name = token]").val()
                    },
                    success: function(result)
                    {
                        if(result == "ok")
                        {
                            alert("Pessoa cadastrada!");
                        }   
                        else
                        {
                            alert("Erro: " + result);
                        }
                    }
                });
            });
        });
        </script>
    </body>

</html>

callMyController.php: would be yours framework or standard architecture, but in case I put it not to have to create an architecture, is this file that the ajax send the request, in it I just validated the token for the first time and send the array data GET for my controller:

<?php
    require_once 'MyController.php';

    if(isset($_GET['token']) and $_GET['token'] === $_SESSION['token'])
    {
        $mc = new MyController();

        $mc->cadastrar($_GET);
    }
    else
        echo "Erro no acesso";

Mycontroller.php: In this class I have the communication between the vision and the model, the same validation of token, I try to put the typed data in the model (us setters there are more validations), and finally try to register this person, if I was successful when registering I display a ok as a return to the ajax, if I didn’t take the validation exception that was launched:

<?php

require_once 'Pessoa.php';

class MyController
{
    private $myModel = null;

    public function __construct()
    {
        $this->myModel = new Pessoa();
    }

    public function cadastrar($data)
    {
        if($data['token'] === $_SESSION['token'])
        {
            try 
            {
                $this->myModel->setNome($data['nome']);
                $this->myModel->setEmail($data['email']);
                $this->myModel->setIdade($data['idade']);

                if($this->myModel->inserirPessoa())
                    echo "ok";
            } 
            catch (Exception $e) 
            {
                echo $e->getMessage();
            }
        }
    }
}

Pessoa.php: represents the Person object, where I have the attributes present in the form, its getters and setters with validations and a method of registering a new person, who in the example just tries to put that person in a array session representing my database:

<?php
session_start();
session_name(crypt("MyAppIDKey"));

class Pessoa
{
    private $nome;
    private $email;
    private $idade;

    public function __construct()
    {
        if(!isset($_SESSION['pessoas']))
            $_SESSION['pessoas'] = array();
    }

    public function setNome($nome)
    {
        if(!stripos(mb_strtolower($nome), " and ") and !stripos(mb_strtolower($nome), " or "))
            $this->nome = $nome;
        else
            throw new Exception("Nome inválido");

    }

    public function setEmail($email)
    {
        if(filter_var($email,  FILTER_VALIDATE_EMAIL))
            $this->email = $email;
        else
            throw new Exception("Email inválido");
    }

    public function setidade($idade)
    {
        if(filter_var($idade,  FILTER_VALIDATE_INT))
            $this->idade = $idade;
        else
            throw new Exception("Idade inválida");
    }

    public function getNome()
    {
        return $this->nome;
    }

    public function getIdade()
    {
        return $this->idade;
    }

    public function getEmail()
    {
        return $this->email;
    }

    public function inserirPessoa()
    {
        try 
        {
            array_push($_SESSION['pessoas'], $this);
            return true;
        } 
        catch (Exception $e) 
        {
            throw new Exception($e->getMessage());
        }
    }
}

Last but not least, I placed a file called php., where I try to hack into the system by adding new data, this would be the action of an insertion attack, where the attacker fills the database with useless information, being able to do even worse things, such as trying a SQL Injection automatic, because normally the invasion is not done from one to a given, but using a repetition structure.

<?php
$_SESSION['token'] = crypt("MyAppIDKey" . time() . "MyTokenSecurity");
?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Tentando invadir</title>
        <script  src="jquery.min.js"></script>
    </head>

    <body>
        <p>
            <label>Token: </label>
            <input type="text" name="token" value="<?php echo $_SESSION['token']; ?>" />
        </p>
        <p><a href="#" class="tryHack" >Invadir</a></p>
        <div class="result"></div>

        <script>
        $(function()
        {

            $("a.tryHack").click(function()
            {
                $.ajax(
                {
                    type: "GET",
                    url: "callMyController.php",
                    data: 
                    {
                        nome: "dado01",
                        idade: "15",
                        email: "[email protected]",
                        token: $("input[name = token]").val()
                    },
                    success: function(result)
                    {
                        if(result == "ok")
                        {
                            alert("Pessoa cadastrada!");
                        }   
                        else
                        {
                            $("div.result").html(result);
                        }
                    }
                });
            });
        });
        </script>
    </body>

</html>

Remarks:

  • Was used the crypt for cryptography, could use other methods, like the md5 (that is not cryptography);
  • It could have more validations, as when arriving at callMyController.php generate another token validation to validate with the controller and in the booster to be generated another in order to be validated on model.
  • I used the method GET just to make it a little more subject to invasions in case someone wanted to try to test invasions via URL. Remembering that the method is recommended POST for this type of transport dice.
  • Methods like md5 when it comes to cryptography is not good nor if you want to give advice to use, because when it comes to security md5 fail,because your hash is easy to mirror and manage to break your security.If you were to use even other methods, I would advise Base64 or crypt as you already used in your reply.

  • 2

    Yes. As I mentioned in the remarks, md5 nor is it a method of cryptography.

3

An ajax request is as secure as a webform request. Both are HTTP requests and the difference between them is the possibility for ajax to carry out asynchronous submissions in addition to server processing in the background.

Otherwise, any security measure you need to take with webforms, you will need to use with ajax.

So the question you have to ask yourself is, what security flaws can my project have?

Illustrating the cited example, in which you want to measure how requests should be made you can use the session to do this, let’s take an example:

Each request you save in the session the moment it is made:

session_start();
$_SESSION['now_request'] = time();

On the page that will receive the ajax request you do the following:

session_start();

// Se não houver uma ultima requisição, então $ultima recebe a hora da requisição atual, caso contrario, recebe a hora da ultima requisição;
$ultima =(empty($_SESSION['last_request']))?$_SESSION['now_request']-301:$_SESSION['last_request'];

// O $tempo recebe a subtração da requisição atual com a hora da requisição da ultima, retornando os segundos passados entre a requisição atual e a ultima requisição
$tempo  = $_SESSION['now_request'] - $ultima;

// Se o tempo for maior que 300(5*60) então se passou o tempo necessário para que o usuário possa fazer uma nova requisição.
if ($tempo > 300){
      //Executa requisição
}
// Caso contrario rejeite-a
else{
      echo 'A proxima requisição poderá se executada em ($tempo - 300) segundos';
}

This example just illustrates your question, there are better ways to do it. The point of the question is, making the request via ajax or webform, the way to process the request will always be the same, as well as your security requirements.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.