Utilisez des instructions préparées et des requêtes paramétrées. Il s'agit d'instructions SQL envoyées et analysées par le serveur de base de données indépendamment de tout paramètre. De cette façon, il est impossible pour un attaquant d'injecter du SQL malveillant.
Vous avez essentiellement deux options pour y parvenir:
Utilisation de PDO (pour tout pilote de base de données pris en charge):
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
$stmt->execute([ 'name' => $name ]);
foreach ($stmt as $row) {
// Do something with $row
}
Utilisation de MySQLi (pour MySQL):
$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
// Do something with $row
}
Si vous vous connectez à une base de données autre que MySQL, il existe une deuxième option spécifique au pilote à laquelle vous pouvez vous référer (par exemple, pg_prepare()
et pg_execute()
pour PostgreSQL). PDO est l'option universelle.
Configuration correcte de la connexion
Notez que lorsque vous utilisez PDO
pour accéder à une base de données MySQL, les instructions réelles préparées ne sont pas utilisées par défaut . Pour résoudre ce problème, vous devez désactiver l'émulation des instructions préparées. Un exemple de création d'une connexion à l'aide de PDO est:
$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'password');
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Dans l'exemple ci-dessus, le mode d'erreur n'est pas strictement nécessaire, mais il est conseillé de l'ajouter . De cette façon, le script ne s'arrêtera pas avec un en Fatal Error
cas de problème. Et cela donne au développeur la possibilité de catch
toute erreur qui est throw
n comme PDOException
s.
Ce qui est obligatoire , cependant, est la première setAttribute()
ligne, qui indique à PDO de désactiver les instructions préparées émulées et d'utiliser de véritables instructions préparées. Cela garantit que l'instruction et les valeurs ne sont pas analysées par PHP avant de les envoyer au serveur MySQL (ne donnant à un attaquant potentiel aucune chance d'injecter du SQL malveillant).
Bien que vous puissiez définir le charset
dans les options du constructeur, il est important de noter que les versions «plus anciennes» de PHP (avant 5.3.6) ignoraient silencieusement le paramètre charset dans le DSN.
Explication
L'instruction SQL à laquelle vous passez prepare
est analysée et compilée par le serveur de base de données. En spécifiant des paramètres (un ?
ou un paramètre nommé comme :name
dans l'exemple ci-dessus), vous indiquez au moteur de base de données sur lequel vous souhaitez filtrer. Ensuite, lorsque vous appelez execute
, l'instruction préparée est combinée avec les valeurs de paramètre que vous spécifiez.
L'important ici est que les valeurs des paramètres sont combinées avec l'instruction compilée, pas une chaîne SQL. L'injection SQL fonctionne en incitant le script à inclure des chaînes malveillantes lorsqu'il crée du SQL à envoyer à la base de données. Ainsi, en envoyant le SQL réel séparément des paramètres, vous limitez le risque de vous retrouver avec quelque chose que vous ne vouliez pas.
Tous les paramètres que vous envoyez lors de l'utilisation d'une instruction préparée seront simplement traités comme des chaînes (bien que le moteur de base de données puisse effectuer une certaine optimisation, les paramètres peuvent également se terminer par des nombres, bien sûr). Dans l'exemple ci-dessus, si la $name
variable contient 'Sarah'; DELETE FROM employees
le résultat serait simplement une recherche de la chaîne "'Sarah'; DELETE FROM employees"
, et vous ne vous retrouverez pas avec une table vide .
Un autre avantage de l'utilisation d'instructions préparées est que si vous exécutez la même instruction plusieurs fois dans la même session, elle ne sera analysée et compilée qu'une seule fois, ce qui vous permettra de gagner en vitesse.
Oh, et puisque vous avez demandé comment le faire pour un encart, voici un exemple (en utilisant PDO):
$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');
$preparedStatement->execute([ 'column' => $unsafeValue ]);
Les instructions préparées peuvent-elles être utilisées pour des requêtes dynamiques?
Bien que vous puissiez toujours utiliser des instructions préparées pour les paramètres de requête, la structure de la requête dynamique elle-même ne peut pas être paramétrée et certaines fonctionnalités de requête ne peuvent pas être paramétrées.
Pour ces scénarios spécifiques, la meilleure chose à faire est d'utiliser un filtre de liste blanche qui restreint les valeurs possibles.
// Value whitelist
// $dir can only be 'DESC', otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
$dir = 'ASC';
}
Si vous utilisez une version récente de PHP, l'
mysql_real_escape_string
option décrite ci-dessous ne sera plus disponible (bien qu'il s'agisse d'mysqli::escape_string
un équivalent moderne). De nos jours, cettemysql_real_escape_string
option n'aurait de sens que pour le code hérité d'une ancienne version de PHP.Vous avez deux options - échapper les caractères spéciaux dans votre
unsafe_variable
, ou utiliser une requête paramétrée. Les deux vous protégeraient de l'injection SQL. La requête paramétrée est considérée comme la meilleure pratique mais nécessitera de passer à une extension MySQL plus récente en PHP avant de pouvoir l'utiliser.Nous couvrirons la chaîne à faible impact qui en échappe une en premier.
Voir aussi les détails de la
mysql_real_escape_string
fonction.Pour utiliser la requête paramétrée, vous devez utiliser MySQLi plutôt que les fonctions MySQL . Pour réécrire votre exemple, nous aurions besoin de quelque chose comme ce qui suit.
La fonction clé que vous voudrez lire là-dessus serait
mysqli::prepare
.En outre, comme d'autres l'ont suggéré, vous pouvez trouver utile / plus facile d'intensifier une couche d'abstraction avec quelque chose comme PDO .
Veuillez noter que le cas que vous avez demandé est assez simple et que les cas plus complexes peuvent nécessiter des approches plus complexes. En particulier:
mysql_real_escape_string
. Dans ce type de cas, vous feriez mieux de passer l'entrée de l'utilisateur via une liste blanche pour vous assurer que seules les valeurs «sûres» sont autorisées.mysql_real_escape_string
approche, vous souffrirez du problème décrit par Polynomial dans les commentaires ci-dessous. Ce cas est plus délicat car les entiers ne seraient pas entourés de guillemets, vous pouvez donc y faire face en validant que l'entrée utilisateur ne contient que des chiffres.la source
mysql_real_escape_string
est suffisant ou je dois aussi utiliser paramétré?htmlentities
par exemplemysql_real_escape_string()
pour être complet, mais je ne suis pas un fan de la liste de l'approche la plus sujette aux erreurs en premier. Le lecteur pourrait simplement saisir rapidement le premier exemple. Heureusement, il est obsolète maintenant :)mysql_*
fonctions sont obsolètes. Ils ont été remplacés par desmysqli_*
fonctions similaires , telles quemysqli_real_escape_string
.Chaque réponse ici ne couvre qu'une partie du problème. En fait, il existe quatre parties de requête différentes que nous pouvons ajouter dynamiquement à SQL: -
Et les déclarations préparées n'en couvrent que deux.
Mais parfois, nous devons rendre notre requête encore plus dynamique, en ajoutant également des opérateurs ou des identifiants. Nous aurons donc besoin de différentes techniques de protection.
En général, une telle approche de protection est basée sur la liste blanche .
Dans ce cas, chaque paramètre dynamique doit être codé en dur dans votre script et choisi dans cet ensemble. Par exemple, pour effectuer un classement dynamique:
Pour faciliter le processus, j'ai écrit une fonction d'aide à la liste blanche qui fait tout le travail sur une seule ligne:
Il existe une autre façon de sécuriser les identifiants - échapper mais je préfère m'en tenir à la liste blanche comme une approche plus robuste et explicite. Pourtant, tant que vous avez un identifiant entre guillemets, vous pouvez échapper le caractère de citation pour le rendre sûr. Par exemple, par défaut pour mysql, vous devez doubler le caractère de citation pour l'échapper . Pour les autres autres règles d'échappement du SGBD, ce serait différent.
Il existe toujours un problème avec les mots clés de syntaxe SQL (tels que
AND
,DESC
et tels), mais la liste blanche semble la seule approche dans ce cas.Ainsi, une recommandation générale peut être formulée comme
Mise à jour
Bien qu'il existe un accord général sur les meilleures pratiques en matière de protection par injection SQL, il existe également de nombreuses mauvaises pratiques. Et certains d'entre eux sont trop profondément ancrés dans l'esprit des utilisateurs de PHP. Par exemple, sur cette même page, il y a (bien qu'invisible pour la plupart des visiteurs) plus de 80 réponses supprimées - toutes supprimées par la communauté en raison d'une mauvaise qualité ou de la promotion de mauvaises pratiques obsolètes. Pire encore, certaines des mauvaises réponses ne sont pas supprimées, mais plutôt prospères.
Par exemple, il y a (1) (2) encore (3) plusieurs (4) réponses (5) , y compris la deuxième réponse la plus votée suggérant que vous échappiez manuellement les chaînes - une approche obsolète qui s'est avérée peu sûre.
Ou il y a une réponse légèrement meilleure qui suggère juste une autre méthode de formatage de chaîne et la revendique même comme la panacée ultime. Bien sûr, ce n'est pas le cas. Cette méthode n'est pas meilleure que le formatage de chaîne normal, mais elle conserve tous ses inconvénients: elle s'applique uniquement aux chaînes et, comme tout autre formatage manuel, c'est essentiellement une mesure facultative et non obligatoire, sujette à des erreurs humaines de toute sorte.
Je pense que tout cela à cause d'une très ancienne superstition, soutenue par des autorités comme OWASP ou le manuel PHP , qui proclame l'égalité entre tout ce qui "s'échappe" et la protection contre les injections SQL.
Indépendamment de ce que le manuel PHP a dit depuis des lustres, cela
*_escape_string
ne sécurise en aucun cas les données et n'a jamais été destiné. En plus d'être inutile pour toute partie SQL autre que la chaîne, l'échappement manuel est incorrect, car il est manuel contrairement à automatisé.Et OWASP aggrave encore la situation, en insistant sur la fuite des entrées utilisateur, ce qui est un non-sens absolu: il ne devrait pas y avoir de tels mots dans le contexte de la protection contre les injections. Chaque variable est potentiellement dangereuse - quelle que soit la source! Ou, en d'autres termes - chaque variable doit être correctement formatée pour être placée dans une requête - quelle que soit la source. C'est la destination qui compte. Au moment où un développeur commence à séparer les moutons des chèvres (en pensant si une variable particulière est "sûre" ou non), il / elle fait son premier pas vers la catastrophe. Sans oublier que même le libellé suggère une fuite en masse au point d'entrée, ressemblant à la fonction de citations très magiques - déjà méprisée, déconseillée et supprimée.
Ainsi, contrairement à tout "échappement", les instructions préparées sont la mesure qui protège effectivement contre l'injection SQL (le cas échéant).
la source
Je recommanderais d'utiliser PDO (PHP Data Objects) pour exécuter des requêtes SQL paramétrées.
Non seulement cela protège contre l'injection SQL, mais cela accélère également les requêtes.
Et en utilisant PDO plutôt que
mysql_
,mysqli_
et lespgsql_
fonctions, vous rendez votre application un peu plus abstraite de la base de données, dans les rares cas où vous devez changer de fournisseur de base de données.la source
Utiliser
PDO
et préparer les requêtes.(
$conn
est unPDO
objet)la source
Comme vous pouvez le voir, les gens vous suggèrent d'utiliser des déclarations préparées au maximum. Ce n'est pas faux, mais lorsque votre requête est exécutée une seule fois par processus, il y aura une légère baisse des performances.
J'étais confronté à ce problème, mais je pense que je l'ai résolu de manière très sophistiquée - la façon dont les pirates informatiques utilisent pour éviter d'utiliser des guillemets. J'ai utilisé ceci en conjonction avec des déclarations préparées émulées. Je l'utilise pour empêcher toutes sortes d'attaques par injection SQL possibles.
Mon approche:
Si vous vous attendez à ce que l'entrée soit un entier, assurez-vous qu'il est vraiment entier. Dans un langage de type variable comme PHP, c'est très important. Vous pouvez utiliser par exemple cette solution très simple mais puissante:
sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);
Si vous attendez autre chose de l' hexagone entier, il . Si vous le sortez, vous échapperez parfaitement à toute entrée. En C / C ++, il existe une fonction appelée
mysql_hex_string()
, en PHP, vous pouvez utiliserbin2hex()
.Ne vous inquiétez pas du fait que la chaîne échappée aura une taille 2x de sa longueur d'origine car même si vous utilisez
mysql_real_escape_string
, PHP doit allouer la même capacité((2*input_length)+1)
, qui est la même.Cette méthode hexadécimale est souvent utilisée lorsque vous transférez des données binaires, mais je ne vois aucune raison de ne pas l'utiliser sur toutes les données pour empêcher les attaques par injection SQL. Notez que vous devez ajouter des données
0x
ou utiliser la fonction MySQL à laUNHEX
place.Ainsi, par exemple, la requête:
Va devenir:
ou
Hex est l'évasion parfaite. Pas moyen de s'injecter.
Différence entre la fonction UNHEX et le préfixe 0x
Il y a eu une discussion dans les commentaires, donc je veux enfin être clair. Ces deux approches sont très similaires, mais elles sont un peu différentes à certains égards:
Le ** 0x ** préfixe ne peut être utilisé pour les colonnes de données telles que char, varchar, texte, bloc, binaire, etc .
De plus, son utilisation est un peu compliquée si vous êtes sur le point d'insérer une chaîne vide. Vous devrez le remplacer entièrement par
''
, ou vous obtiendrez une erreur.UNHEX () fonctionne sur n'importe quelle colonne; vous n'avez pas à vous soucier de la chaîne vide.
Les méthodes hexagonales sont souvent utilisées comme attaques
Notez que cette méthode hexadécimale est souvent utilisée comme une attaque par injection SQL où les entiers sont comme des chaînes et échappés juste avec
mysql_real_escape_string
. Ensuite, vous pouvez éviter l'utilisation de guillemets.Par exemple, si vous faites simplement quelque chose comme ceci:
une attaque peut vous injecter très facilement . Considérez le code injecté suivant renvoyé par votre script:
et maintenant il suffit d'extraire la structure de la table:
Et puis sélectionnez simplement les données que vous souhaitez. N'est-ce pas cool?
Mais si le codeur d'un site injectable le sortait, aucune injection ne serait possible car la requête ressemblerait à ceci:
SELECT ... WHERE id = UNHEX('2d312075...3635')
la source
+
mais avecCONCAT
. Et pour les performances: je ne pense pas que cela affecte les performances car mysql doit analyser les données et peu importe si l'origine est une chaîne ou un hex'root'
ou vous pouvez l'enserrer0x726f6f74
MAIS si vous voulez un nombre et l'envoyer sous forme de chaîne, vous écrirez probablement '42' et non CHAR (42 ) ... '42' dans l'hex ne serait0x3432
pas0x42
SELECT title FROM article WHERE id = UNHEX(' . bin2hex($_GET["id"]) . ')
Prévention des injections - mysql_real_escape_string ()
PHP a une fonction spécialement conçue pour empêcher ces attaques. Tout ce que vous avez à faire est d'utiliser la fonction d'une bouchée
mysql_real_escape_string
.mysql_real_escape_string
prend une chaîne qui va être utilisée dans une requête MySQL et retourne la même chaîne avec toutes les tentatives d'injection SQL échappées en toute sécurité. Fondamentalement, il remplacera les guillemets gênants (') qu'un utilisateur pourrait entrer avec un substitut sûr MySQL, un guillemet échappé \'.REMARQUE: vous devez être connecté à la base de données pour utiliser cette fonction!
// Connectez-vous à MySQL
Vous pouvez trouver plus de détails dans MySQL - SQL Injection Prevention .
la source
mysql_real_escape_string
but est de permettre de construire une requête SQL correcte pour chaque chaîne de données d'entrée. La prévention sql-injection est l'effet secondaire de cette fonction.mysql_real_escape_string()
n'est pas infaillible .mysql_real_escape_string
est désormais obsolète, ce n'est donc plus une option viable. Il sera supprimé à l'avenir de PHP. Il est préférable de passer à ce que les gens de PHP ou de MySQL recommandent.Vous pouvez faire quelque chose de basique comme ceci:
Cela ne résoudra pas tous les problèmes, mais c'est un très bon tremplin. J'ai omis des éléments évidents tels que la vérification de l'existence, du format de la variable (chiffres, lettres, etc.).
la source
$q = "SELECT col FROM tbl WHERE x = $safe_var";
par exemple. Réglage$safe_var
de1 UNION SELECT password FROM users
travaux dans ce cas en raison du manque de citations. Il est également possible d'injecter des chaînes dans la requête à l'aide deCONCAT
etCHR
.mysql_real_escape_string()
n'est pas infaillible .mysql_real_escape_string
est désormais obsolète, ce n'est donc plus une option viable. Il sera supprimé à l'avenir de PHP. Il est préférable de passer à ce que les gens de PHP ou de MySQL recommandent.Quoi que vous finissiez par utiliser, assurez-vous de vérifier que votre entrée n'a pas déjà été altérée par
magic_quotes
ou d'autres déchets bien intentionnés, et si nécessaire, exécutez-lastripslashes
ou autre chose pour la désinfecter.la source
La requête paramétrée ET la validation des entrées est la voie à suivre. Il existe de nombreux scénarios dans lesquels une injection SQL peut se produire, même si elle
mysql_real_escape_string()
a été utilisée.Ces exemples sont vulnérables à l'injection SQL:
ou
Dans les deux cas, vous ne pouvez pas utiliser
'
pour protéger l'encapsulation.Source : l' injection SQL inattendue (lorsque l'échappement ne suffit pas)
la source
À mon avis, la meilleure façon d'empêcher généralement l'injection SQL dans votre application PHP (ou n'importe quelle application Web, d'ailleurs) est de penser à l'architecture de votre application. Si la seule façon de se protéger contre l'injection SQL est de se souvenir d'utiliser une méthode ou une fonction spéciale qui fait la bonne chose chaque fois que vous parlez à la base de données, vous vous trompez. De cette façon, ce n'est qu'une question de temps jusqu'à ce que vous oubliez de formater correctement votre requête à un moment donné de votre code.
L'adoption du modèle MVC et d'un cadre comme CakePHP ou CodeIgniter est probablement la bonne solution: des tâches courantes telles que la création de requêtes de base de données sécurisées ont été résolues et mises en œuvre de manière centralisée dans de tels cadres. Ils vous aident à organiser votre application Web de manière sensée et vous font penser davantage au chargement et à l'enregistrement d'objets qu'à la construction sécurisée de requêtes SQL uniques.
la source
Je privilégie les procédures stockées ( MySQL prend en charge les procédures stockées depuis 5.0 ) d'un point de vue sécurité - les avantages sont -
Les inconvénients sont -
la source
Il existe de nombreuses façons d'empêcher les injections SQL et autres hacks SQL. Vous pouvez facilement le trouver sur Internet (Recherche Google). Bien sûr, PDO est l'une des bonnes solutions. Mais je voudrais vous suggérer une bonne prévention des liens contre l'injection SQL.
Qu'est-ce que l'injection SQL et comment empêcher
Manuel PHP pour l'injection SQL
Explication de Microsoft sur l'injection et la prévention SQL en PHP
Et quelques autres comme Empêcher l'injection SQL avec MySQL et PHP .
Maintenant, pourquoi avez-vous besoin d'empêcher votre requête d'injection SQL?
Je voudrais vous faire savoir: pourquoi essayons-nous d'empêcher l'injection SQL avec un court exemple ci-dessous:
Requête pour la correspondance d'authentification de connexion:
Maintenant, si quelqu'un (un hacker) met
et mot de passe quoi que ce soit ....
La requête ne sera analysée dans le système que jusqu'à:
L'autre partie sera jetée. Alors, que va-t-il se passer? Un utilisateur non autorisé (hacker) pourra se connecter en tant qu'administrateur sans avoir son mot de passe. Maintenant, il / elle peut faire tout ce que l'administrateur / email peut faire. Voir, c'est très dangereux si l'injection SQL n'est pas empêchée.
la source
Je pense que si quelqu'un veut utiliser PHP et MySQL ou un autre serveur dataBase:
(int)$foo
. En savoir plus sur le type de variables en PHP ici . Si vous utilisez des bibliothèques telles que PDO ou MySQLi, utilisez toujours PDO :: quote () et mysqli_real_escape_string () .Exemples de bibliothèques:
---- AOP
--- MySQLi
PS :
AOP remporte cette bataille avec facilité. Avec la prise en charge de douze pilotes de base de données différents et de paramètres nommés, nous pouvons ignorer la petite perte de performances et nous habituer à son API. Du point de vue de la sécurité, les deux sont sûrs tant que le développeur les utilise comme ils sont censés être utilisés
Mais alors que PDO et MySQLi sont assez rapides, MySQLi fonctionne de manière insignifiante plus rapidement dans les benchmarks - ~ 2,5% pour les instructions non préparées et ~ 6,5% pour les instructions préparées.
Et veuillez tester chaque requête dans votre base de données - c'est une meilleure façon d'empêcher l'injection.
la source
Si possible, transtypez les types de vos paramètres. Mais cela ne fonctionne que sur des types simples comme int, bool et float.
la source
Si vous souhaitez profiter des moteurs de cache, comme Redis ou Memcached , peut-être que DALMP pourrait être un choix. Il utilise MySQLi pur . Vérifiez ceci: DALMP Database Abstraction Layer for MySQL en utilisant PHP.
En outre, vous pouvez «préparer» vos arguments avant de préparer votre requête afin de pouvoir créer des requêtes dynamiques et, à la fin, avoir une requête d'instructions entièrement préparée. Couche d'abstraction de base de données DALMP pour MySQL utilisant PHP.
la source
Pour ceux qui ne savent pas comment utiliser PDO (provenant des
mysql_
fonctions), j'ai fait un wrapper PDO très, très simple qui est un seul fichier. Il existe pour montrer à quel point il est facile de faire toutes les choses courantes que les applications doivent être faites. Fonctionne avec PostgreSQL, MySQL et SQLite.Fondamentalement, le lire pendant que vous lisez le manuel pour voir comment mettre les fonctions AOP à l' utilisation dans la vie réelle pour le rendre simple pour stocker et récupérer des valeurs dans le format que vous voulez.
la source
En utilisant cette fonction PHP,
mysql_escape_string()
vous pouvez obtenir une bonne prévention rapidement.Par exemple:
mysql_escape_string
- Échappe une chaîne pour une utilisation dans une mysql_queryPour plus de prévention, vous pouvez ajouter à la fin ...
Enfin, vous obtenez:
la source
Quelques conseils pour échapper les caractères spéciaux dans les instructions SQL.
N'utilisez pas MySQL . Cette extension est obsolète. Utilisez plutôt MySQLi ou PDO .
MySQLi
Pour échapper manuellement des caractères spéciaux dans une chaîne, vous pouvez utiliser la fonction mysqli_real_escape_string . La fonction ne fonctionnera pas correctement sauf si le jeu de caractères correct est défini avec mysqli_set_charset .
Exemple:
Pour l'échappement automatique de valeurs avec des instructions préparées, utilisez mysqli_prepare et mysqli_stmt_bind_param où des types pour les variables de liaison correspondantes doivent être fournis pour une conversion appropriée:
Exemple:
Peu importe si vous utilisez des instructions préparées ou
mysqli_real_escape_string
, vous devez toujours connaître le type de données d'entrée avec lesquelles vous travaillez.Donc, si vous utilisez une instruction préparée, vous devez spécifier les types de variables pour la
mysqli_stmt_bind_param
fonction.Et l'utilisation de
mysqli_real_escape_string
est pour, comme son nom l'indique, échapper des caractères spéciaux dans une chaîne, donc cela ne rendra pas les entiers sûrs. Le but de cette fonction est d'empêcher la rupture des chaînes dans les instructions SQL et les dommages à la base de données qu'elle pourrait provoquer.mysqli_real_escape_string
est une fonction utile lorsqu'elle est utilisée correctement, en particulier lorsqu'elle est combinée avecsprintf
.Exemple:
la source
L'alternative simple à ce problème pourrait être résolue en accordant les autorisations appropriées dans la base de données elle-même. Par exemple: si vous utilisez une base de données MySQL, entrez dans la base de données via le terminal ou l'interface utilisateur fournie et suivez simplement cette commande:
Cela limitera l'utilisateur à se limiter uniquement aux requêtes spécifiées. Supprimez l'autorisation de suppression afin que les données ne soient jamais supprimées de la requête renvoyée depuis la page PHP. La deuxième chose à faire est de vider les privilèges afin que MySQL actualise les autorisations et les mises à jour.
plus d'informations sur flush .
Pour voir les privilèges actuels de l'utilisateur, lancez la requête suivante.
En savoir plus sur GRANT .
la source
En ce qui concerne de nombreuses réponses utiles, j'espère ajouter de la valeur à ce fil.
L'injection SQL est une attaque qui peut être effectuée via des entrées utilisateur (entrées remplies par un utilisateur, puis utilisées dans des requêtes). Les modèles d'injection SQL sont une syntaxe de requête correcte alors que nous pouvons l'appeler: mauvaises requêtes pour de mauvaises raisons, et nous supposons qu'une mauvaise personne peut essayer d'obtenir des informations secrètes (en contournant le contrôle d'accès) qui affectent les trois principes de sécurité (confidentialité , intégrité et disponibilité).
Maintenant, notre objectif est de prévenir les menaces de sécurité telles que les attaques par injection SQL, la question demandant (comment empêcher une attaque par injection SQL en utilisant PHP), être plus réaliste, le filtrage ou la suppression des données d'entrée est le cas lors de l'utilisation de données d'entrée utilisateur à l'intérieur une telle requête, en utilisant PHP ou tout autre langage de programmation n'est pas le cas, ou comme recommandé par plus de gens pour utiliser une technologie moderne comme une instruction préparée ou tout autre outil prenant actuellement en charge la prévention des injections SQL, considérez-vous que ces outils ne sont plus disponibles? Comment sécurisez-vous votre application?
Mon approche contre l'injection SQL est la suivante: effacer les données entrées par l'utilisateur avant de les envoyer à la base de données (avant de les utiliser dans une requête).
Filtrage des données pour (conversion de données non sécurisées en données sécurisées)
Considérez que PDO et MySQLi ne sont pas disponibles. Comment sécuriser votre application? Me forcez-vous à les utiliser? Qu'en est-il des autres langages autres que PHP? Je préfère fournir des idées générales car elles peuvent être utilisées pour une frontière plus large, pas seulement pour une langue spécifique.
RÈGLE: ne créez pas un utilisateur de base de données pour tous les privilèges. Pour toutes les opérations SQL, vous pouvez créer votre schéma comme (deluser, selectuser, updateuser) en tant que noms d'utilisateur pour une utilisation facile.
Voir principe du moindre privilège .
Filtrage des données: avant de créer une entrée utilisateur de requête, elle doit être validée et filtrée. Pour les programmeurs, il est important de définir certaines propriétés pour chaque variable entrée par l'utilisateur: type de données, modèle de données et longueur des données . Un champ qui est un nombre entre (x et y) doit être exactement validé en utilisant la règle exacte, et pour un champ qui est une chaîne (texte): le modèle est le cas, par exemple, un nom d'utilisateur ne doit contenir que quelques caractères, disons dites [a-zA-Z0-9_-.]. La longueur varie entre (x et n) où x et n (entiers, x <= n). Règle: la création de filtres exacts et de règles de validation sont les meilleures pratiques pour moi.
Utilisez d'autres outils: ici, je conviendrai également avec vous qu'une instruction préparée (requête paramétrée) et des procédures stockées. Les inconvénients ici sont que ces méthodes nécessitent des compétences avancées qui n'existent pas pour la plupart des utilisateurs. L'idée de base ici est de faire la distinction entre la requête SQL et les données utilisées à l'intérieur. Les deux approches peuvent être utilisées même avec des données non sécurisées, car les données saisies par l'utilisateur ici n'ajoutent rien à la requête d'origine, comme (any ou x = x).
Pour plus d'informations, veuillez lire le cheat sheet OWASP SQL Injection Prevention .
Maintenant, si vous êtes un utilisateur avancé, commencez à utiliser cette défense comme vous le souhaitez, mais, pour les débutants, s'ils ne peuvent pas rapidement mettre en œuvre une procédure stockée et préparer l'instruction, il est préférable de filtrer autant que possible les données d'entrée.
Enfin, considérons qu'un utilisateur envoie ce texte ci-dessous au lieu de saisir son nom d'utilisateur:
Cette entrée peut être vérifiée tôt sans instruction préparée ni procédures stockées, mais pour être sûr, leur utilisation commence après le filtrage et la validation des données utilisateur.
Le dernier point est la détection d'un comportement inattendu qui nécessite plus d'efforts et de complexité; ce n'est pas recommandé pour les applications Web normales.
Un comportement inattendu dans l'entrée utilisateur ci-dessus est SELECT, UNION, IF, SUBSTRING, BENCHMARK, SHA et root. Une fois ces mots détectés, vous pouvez éviter la saisie.
MISE À JOUR 1:
Un utilisateur a commenté que ce post est inutile, OK! Voici ce que OWASP.ORG a fourni :
Comme vous le savez peut-être, la revendication d'un article doit être appuyée par un argument valide, au moins par une référence! Sinon, c'est considéré comme une attaque et une mauvaise réclamation!
Mise à jour 2:
Depuis le manuel PHP, PHP: Instructions préparées - Manuel :
Mise à jour 3:
J'ai créé des cas de test pour savoir comment PDO et MySQLi envoient la requête au serveur MySQL lors de l'utilisation d'une instruction préparée:
AOP:
Journal des requêtes:
MySQLi:
Journal des requêtes:
Il est clair qu'une instruction préparée échappe également aux données, rien d'autre.
Comme mentionné également dans la déclaration ci-dessus,
Par conséquent, cela prouve que la validation des données telle que
intval()
c'est une bonne idée pour les valeurs entières avant d'envoyer une requête. De plus, empêcher les données utilisateur malveillantes avant d'envoyer la requête est une approche correcte et valide .Veuillez consulter cette question pour plus de détails: PDO envoie une requête brute à MySQL tandis que Mysqli envoie une requête préparée, les deux produisent le même résultat
Références:
la source
J'utilise trois façons différentes pour empêcher mon application Web d'être vulnérable à l'injection SQL.
mysql_real_escape_string()
, qui est une fonction prédéfinie dans PHP , et ce module de code anti - slash aux caractères suivants:\x00
,\n
,\r
,\
,'
,"
et\x1a
. Passez les valeurs d'entrée en tant que paramètres pour minimiser les risques d'injection SQL.J'espère que cela t'aidera.
Considérez la requête suivante:
$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";
mysql_real_escape_string () ne protège pas ici. Si vous utilisez des guillemets simples ('') autour de vos variables dans votre requête, c'est ce qui vous protège contre cela. Voici une solution ci-dessous pour cela:
$iId = (int) mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";
Cette question a de bonnes réponses à ce sujet.
Je suggère que l'utilisation de PDO est la meilleure option.
Éditer:
mysql_real_escape_string()
est obsolète depuis PHP 5.5.0. Utilisez mysqli ou PDO.Une alternative à mysql_real_escape_string () est
Exemple:
la source
Un moyen simple serait d'utiliser un framework PHP comme CodeIgniter ou Laravel qui ont des fonctionnalités intégrées comme le filtrage et l'enregistrement actif afin que vous n'ayez pas à vous soucier de ces nuances.
la source
Avertissement: l'approche décrite dans cette réponse ne s'applique qu'à des scénarios très spécifiques et n'est pas sécurisée car les attaques par injection SQL ne reposent pas uniquement sur la possibilité d'injecter
X=Y
.Si les attaquants tentent de pirater le formulaire via la
$_GET
variable PHP ou avec la chaîne de requête de l'URL, vous seriez en mesure de les attraper s'ils ne sont pas sécurisés.Parce que
1=1
,2=2
,1=2
,2=1
,1+1=2
, etc ... sont les questions communes à une base de données SQL d'un attaquant. Peut-être aussi qu'il est utilisé par de nombreuses applications de piratage.Mais vous devez être prudent, que vous ne devez pas réécrire une requête sûre depuis votre site. Le code ci-dessus vous donne un conseil, pour réécrire ou rediriger (cela dépend de vous) cette chaîne de requête dynamique spécifique au piratage dans une page qui stockera l' adresse IP de l'attaquant , ou MÊME LEURS COOKIES, l'historique, le navigateur ou tout autre élément sensible informations, afin que vous puissiez les traiter plus tard en interdisant leur compte ou en contactant les autorités.
la source
1-1=0
? :)([0-9\-]+)=([0-9]+)
.Il y a tellement de réponses pour PHP et MySQL , mais voici du code pour PHP et Oracle pour empêcher l'injection SQL ainsi que l'utilisation régulière des pilotes oci8:
la source
Une bonne idée est d'utiliser un mappeur relationnel objet comme Idiorm :
Cela vous évite non seulement les injections SQL, mais aussi les erreurs de syntaxe! Il prend également en charge des collections de modèles avec un chaînage de méthodes pour filtrer ou appliquer des actions à plusieurs résultats à la fois et à plusieurs connexions.
la source
L'utilisation de PDO et MYSQLi est une bonne pratique pour empêcher les injections SQL, mais si vous voulez vraiment travailler avec les fonctions et requêtes MySQL, il serait préférable d'utiliser
mysql_real_escape_string
Il y a plus de capacités pour empêcher cela: comme identifier - si l'entrée est une chaîne, un nombre, un caractère ou un tableau, il y a tellement de fonctions intégrées pour détecter cela. En outre, il serait préférable d'utiliser ces fonctions pour vérifier les données d'entrée.
is_string
is_numeric
Et il est tellement préférable d'utiliser ces fonctions pour vérifier les données d'entrée avec
mysql_real_escape_string
.la source
mysql_real_escape_string()
n'est pas infaillible .mysql_real_escape_string
est désormais obsolète, ce n'est donc plus une option viable. Il sera supprimé de PHP à l'avenir. Il est préférable de passer à ce que les gens de PHP ou de MySQL recommandent.J'ai écrit cette petite fonction il y a plusieurs années:
Cela permet d'exécuter des instructions dans une chaîne C # à une ligne. Format comme:
Il s'échappe en considérant le type de variable. Si vous essayez de paramétrer les noms de table et de colonne, cela échouera car il place chaque chaîne entre guillemets, ce qui n'est pas une syntaxe valide.
MISE À JOUR DE SÉCURITÉ: La
str_replace
version précédente autorisait les injections en ajoutant {#} jetons dans les données utilisateur. Cettepreg_replace_callback
version ne pose pas de problème si le remplacement contient ces jetons.la source