Comment envoyer des requêtes AJAX sécurisées avec PHP et jQuery

Le problème

donc depuis un moment maintenant j'expérimente avec différentes approches AJAX en envoyant des données à un serveur qui seront traitées et stockées dans une base de données MySQL.

La page de la requête AJAX hits api.php, utilise les déclarations préparées par PHP pour enregistrer les données, donc les injections MySQL ne sont pas vraiment un problème et les mots de passe ou les données qui doivent être cryptées sont aussi traités par api.php ce qui n'est pas ce que je demande ici. Ma question concerne davantage la façon de s'assurer que les données sont sécurisées lors du transfert du client vers le serveur.

Les approches

j'ai actuellement (pour la connexion exemple, j'ai inclus ci-dessous):

  • SSL Cert/HTTPS tournant sur le domaine.
  • certaines requêtes AJAX (évidemment pas cet exemple de requête de connexion car il n'y a pas de session pour commencer) ne fonctionneront que si la Session PHP est valide sur tout le site (utilisé sur les deux login.php et api.php dans cet exemple).
  • limitation de vitesse sur api.php lors de l'accès aux fonctions.
  • PHP PDO a préparé des déclarations lors de l'interaction avec la base de données à l'intérieur de api.php.
  • le cryptage des données sensibles à l'intérieur de api.php (sans rapport avec la question).

les questions

Enfin, mes questions sont:

  1. est-ce que cette approche pour utiliser des requêtes HTTP (Ajax) asynchrones est suffisamment sûre pour être utilisée plutôt que de simplement soumettre données vers une page PHP et redirection en avant? (Ce qui améliore l'expérience de l'utilisateur).
  2. Comment puis-je vérifier si les données que mes utilisateurs envoient n'ont pas été altérées?
  3. Suis-je raisonnablement faire assez pour protéger mes données de l'utilisateur, sinon, que puis-je faire?

exemple

je comprends que chacun a des approches différentes pour traiter les données de son site et les transporter. Je comprends aussi que quoi qu'il arrive vous le faites, vous ne pouvez jamais être protégé à 100%, car il peut y avoir des vulnérabilités et des façons de contourner votre système dont vous ne pouvez pas tenir compte. Je suis à la recherche de commentaires/améliorations sur mon approche générale de l'envoi de données en toute sécurité plutôt que la critique du code spécifique ci-dessous car il ne s'agit que d'un exemple. Mais toute réponse constructive est la bienvenue. Merci d'avoir pris le temps de lire/répondre.

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"
    });
}
22
demandé sur LukeXF 2016-06-20 02:37:09

3 réponses

1. Cochez L'en-tête D'origine

spécifié par OWASP, ce n'est pas assez, mais recommandé :

bien qu'il soit trivial de mystifier un en-tête de votre propre navigateur, il est généralement impossible de le faire dans une attaque CSRF, sauf via une vulnérabilité XSS. C'est pourquoi vérifier les en-têtes est un premier pas raisonnable dans votre défense CSRF, mais comme ils ne sont pas toujours présents, son généralement pas considéré comme une défense suffisante sur son propre.

Et Mozilla:

L'en-tête Origin est considéré comme utile contre le vol de données JSON et les attaques CSRF. Les informations fournies par Origin--un peu d'informations contextuelles sur la création de requêtes--devraient fournir des conseils aux serveurs Web sur la fiabilité des requêtes [...]

vérification de la HTTP_ORIGIN en-tête peut être écrite comme :

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) Vérifier le REFERER l'en-tête

from OWASP:

si L'en-tête Origin n'est pas présent, vérifiez que le nom d'hôte dans l'en-tête Referer correspond à l'origine du site. La vérification du referer est une méthode couramment utilisée pour empêcher le CSRF sur les périphériques réseau intégrés car elle ne nécessite pas d'état par utilisateur.. Cette méthode d'atténuation CSRF est aussi couramment utilisée pour les demandes non authentifiées [...]

vérification de la HTTP_REFERER est aussi assez simple en PHP avec $_SERVER['HTTP_REFERER'], vous pouvez simplement mettre à jour le code ci-dessus avec elle.


BE CAREFUL avec la vérification qui a toujours besoin d'être très précis : ne pas cocher juste example.com ou api.example.com mais le plein https://example.com. Pourquoi ? Parce que vous pourriez falsifier ce chèque avec une origine comme api.example.com.hacker.com.


2. Générer des tokens CSRF

bien expliqué réponse spécifique à PHP a été donné il y a en bref :

  1. Générer le jeton :

    session_start();
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    
  2. l'Ajouter dans votre "vues" par l'intermédiaire d'un méta (comme Github) :

    <meta name="csrf-token" content="<?= $_SESSION['csrf_token'] ?>">
    
  3. configurer les appels ajax de jQuery pour inclure ce token :

    $.ajaxSetup({
        headers : {
            'CsrfToken': $('meta[name="csrf-token"]').attr('content')
        }
    });
    
  4. côté serveur vérifiez vos requêtes AJAX:

    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.']));
    }
    

la plupart des cadres PHP ont leur propre implémentation CSRF, qui repose plus ou moins sur le même principe.


3. Désinfecter valider les entrées de l'utilisateur.

Vous devez filtre espace des entrées et valider .


4. Protégez votre serveur


5. Ne jamais faire confiance à l'utilisateur

comme @blue112 l'a dit, c'est l'un des plus élémentaires principes de sécurité.

enter image description here

41
répondu Ivan Gabriele 2017-05-23 12:02:41

brève réponse: vous ne pouvez pas protéger votre client.

réponse:

  • utiliser AJAX est tout aussi sûr que de poster des données avec, par exemple, un formulaire.
  • L'utilisation de HTTPS empêche man-in-the-middle de voir vos données utilisateur, de sorte que les données réelles envoyées par l'utilisateur sont en sécurité.

vous ne pouvez rien faire pour que le navigateur prouve que c'est en fait votre code javascript qui tourne du côté du client. Ensuite, l'action évidente à prendre est la plus simple: NE FAITES JAMAIS CONFIANCE AUX DONNÉES DE L'UTILISATEUR.

cela signifie, Comme vous avez commencé à le faire, sécuriser votre côté serveur en utilisant session, rate limiting, data validation, fail2ban (banning a client ip after a certain number of fail), log monitoring...

1
répondu blue112 2016-06-22 08:23:41

la meilleure façon est encore de faire la sécurisation côté serveur et si j'étais un utilisateur connecté sur le site, je pourrais aussi vérifier le jeton csrf sur ma balise meta et forger un script pour le serveur. Donc le pari sûr est la validation côté serveur

0
répondu Osemwota Osagie Anthony 2017-07-18 14:48:50