Y a-t-il quelque chose qui peut être mis après la clause "ORDER BY" qui peut poser un risque de sécurité?

Fondamentalement, ce que je veux faire est ceci:

mysql_query("SELECT ... FROM ... ORDER BY $_GET[order]")

Ils peuvent évidemment facilement créer une erreur SQL en y mettant un non-sens, mais mysql_query vous permet seulement d'exécuter 1 requête, donc ils ne peuvent pas mettre quelque chose comme 1; DROP TABLE ....

Y a-t-il des dommages qu'un utilisateur malveillant pourrait faire, autres que la création d'une erreur de syntaxe?

Si oui, comment puis-je assainir la requête?

Il y a beaucoup de logique construite sur la variable $_GET['order'] étant dans la syntaxe de type SQL, donc je ne veux vraiment pas changer la format.


Pour clarifier, $_GET['order'] ne sera pas seulement un simple champ/colonne. Cela pourrait être quelque chose comme last_name DESC, first_name ASC.

24
demandé sur Paul Sonier 2011-07-11 22:01:32

5 réponses

Oui, les attaques par injection SQL peuvent utiliser une clause ORDER BY Non échappée comme vecteur. Il y a une explication de la façon dont cela peut être exploité et comment éviter ce problème ici:

Http://josephkeeler.com/2009/05/php-security-sql-injection-in-order-by/

Ce billet de blog recommande d'utiliser une liste blanche pour valider le paramètre ORDER BY, ce qui est presque certainement l'approche la plus sûre.


Pour répondre à la mise à jour, même si la clause est complexe, vous pouvez toujours écrire une routine qui la valide contre une liste blanche, par exemple:

function validate_order_by($order_by_parameter) {
    $columns = array('first_name', 'last_name', 'zip', 'created_at');

    $parts = preg_split("/[\s,]+/", $order_by_parameter);

    foreach ($parts as $part) {
        $subparts = preg_split("/\s+/", $part);

        if (count($subparts) < 0 || count($subparts) > 2) {
           // Too many or too few parts.
           return false;
        }

        if (!in_array($subparts[0], $columns)) {
           // Column name is invalid.
           return false;
        }

        if (count($subparts) == 2 
            && !in_array(strtoupper($subparts[1]), array('ASC', 'DESC')) {
          // ASC or DESC is invalid
          return false;
        }
    }

    return true;
}

Même si la clause ORDER BY est complexe, elle est toujours faite uniquement à partir des valeurs que vous fournissez (en supposant que vous ne laissez pas les utilisateurs l'éditer à la main). Vous pouvez toujours valider à l'aide d'une liste blanche.

Je devrais également ajouter que je n'aime normalement pas exposer ma structure de base de données dans les URL ou d'autres endroits de l'interface utilisateur et je vais souvent alias les choses dans les paramètres dans les URL et le mapper aux valeurs réelles en utilisant un hachage.

30
répondu Rafe 2011-07-11 19:13:39

Ne comptez pas sur le fait qu'une injection SQL à ce stade ne causera actuellement aucun problème; n'autorisez aucune injection SQL. Si rien d'autre, un attaquant malveillant pourrait définir un ordre très complexe qui pourrait causer un ralentissement sérieux de votre base de données.

17
répondu Paul Sonier 2011-07-11 18:04:52

Je préfère faire une liste blanche et traiter la chaîne de paramètre http comme distincte de la chaîne qui est interpolée dans la requête SQL.

Dans L'exemple PHP suivant, les clés du tableau seraient les valeurs transmises en tant que paramètres http, une sorte d'étiquette symbolique pour différents schémas de classement, selon votre interface web. Les valeurs du tableau seraient ce que nous voulons interpoler dans SQL pour ces schémas d'ordre correspondants, par exemple les noms de colonnes ou expression.

<?php

$orderby_whitelist = array(
  "name"    => "last_name, first_name",
  "date"    => "date_created",
  "daterev" => "date_created DESC",
  "DEFAULT" => "id"
);

$order = isset($_GET["order"]) 
  ? $_GET["order"] 
  : "DEFAULT";
$order_expr = array_key_exists($order, $orderby_whitelist) 
  ? $orderby_whitelist[$order] 
  : $orderby_whitelist["DEFAULT"];

mysql_query("SELECT ... FROM ... ORDER BY $order_expr")

Cela a des avantages:

  • Vous pouvez vous défendre contre L'injection SQL même dans les cas où vous ne pouvez pas utiliser les paramètres de requête. Si le client transmet une valeur non reconnue, votre code l'ignore et utilise un ordre par défaut.

  • Vous n'avez rien à désinfecter, car les clés et les valeurs du tableau sont toutes deux écrites par vous, le programmeur. L'entrée du Client ne peut choisir qu'un des choix que vous autorisez.

  • Votre interface web fait ne pas révéler la structure de votre base de données.

  • Vous pouvez faire des commandes personnalisées qui correspondent à des expressions SQL ou à des ASC/DESC alternatifs, comme je l'ai montré ci-dessus.

  • Vous pouvez modifier la structure de la base de données sans modifier votre interface web, ou vice versa.

Je couvre cette solution dans ma présentation, Injection SQL Mythes et les idées fausses, et aussi dans mon livre, SQL Antipatterns: Éviter les Pièges de la Programmation de Base de données.

6
répondu Bill Karwin 2011-07-11 21:57:46

La logique construite autour de la variable $_GET['order'] devrait être tout aussi valide lorsqu'elle est utilisée sur une variable $order_sanitized. Pourquoi ne pas simplement désinfecter l'entrée d'abord et avant tout, puis effectuer votre logique pour la faire entrer dans l'instruction SELECT?

2
répondu Justin ᚅᚔᚈᚄᚒᚔ 2011-07-11 19:37:21

C'est toujours un point D'injection SQL potentielle. Vous dépendez de l'implémentation interne de mysql_query. Que faire si dans une version ultérieure ils le changent pour utiliser plusieurs requêtes?

Vous pouvez utiliser mysql_real_escape_string.

-1
répondu Daniel A. White 2011-07-11 18:03:10