【Sécurité du réseau】Explication détaillée de l'injection SQL

1. Qu'est-ce que l'injection SQL

L'injection SQL est l'une des méthodes d'attaque réseau les plus courantes. Il n'utilise pas le BUG du système d'exploitation pour réaliser l'attaque, mais cible la négligence des programmeurs lors de l'écriture, et réalise une connexion sans compte via des instructions SQL, et même falsifie la base de données.

 2. L'idée générale de l'attaque par injection SQL 

  1 : Trouver l'emplacement de l'injection SQL

  2 : déterminer le type de serveur et le type de base de données d'arrière-plan

  3 : Attaques par injection SQL contre différentes caractéristiques de serveur et de base de données

 3. Exemples d'attaques par injection SQL

String sql = "select * from user_table where username=
' "+userName+" ' and password=' "+password+" '";

--当输入了上面的用户名和密码,上面的SQL语句变成:
SELECT * FROM user_table WHERE username=
'’or 1 = 1 -- and password='’

"""
--分析SQL语句:
--条件后面username=”or 1=1 用户名等于 ” 或1=1 那么这个条件一定会成功;

--然后后面加两个-,这意味着注释,它将后面的语句注释,让他们不起作用,这样语句永远都--能正确执行,用户轻易骗过系统,获取合法身份。
--这还是比较温柔的,如果是执行
SELECT * FROM user_table WHERE
username='' ;DROP DATABASE (DB Name) --' and password=''
--其后果可想而知…
"""

4. Comment se défendre contre l'injection SQL

Remarque : Tout programme présentant une vulnérabilité d'injection SQL est dû au fait que le programme accepte une entrée de variable par l'utilisateur client ou un paramètre transmis par l'URL, et que cette variable ou ce paramètre fait partie de l'instruction SQL. Pour le contenu saisi par l'utilisateur ou le paramètre passé , nous devons toujours être vigilants. C'est le principe "les données externes ne sont pas dignes de confiance" dans le domaine de la sécurité. En examinant les différentes méthodes d'attaque dans le domaine de la sécurité Web, la plupart d'entre elles sont causées par des développeurs qui violent ce principe, alors Quoi On peut naturellement penser à commencer par la détection, le filtrage et la vérification des variables pour s'assurer que les variables correspondent à ce que le développeur attendait.

1. Vérifiez le type et le format des données variables

Si votre instruction SQL se présente sous la forme où id={$id} et que tous les identifiants de la base de données sont des nombres, vous devez vérifier que la variable id est de type int avant l'exécution du SQL ; s'il accepte les boîtes aux lettres , puis Vous devez vérifier et vous assurer strictement que la variable doit être au format de la boîte aux lettres. D'autres types tels que la date et l'heure sont également les mêmes. Pour résumer : tant qu'il existe une variable avec un format fixe, avant l'exécution de l'instruction SQL, il convient de la vérifier strictement selon le format fixe pour s'assurer que la variable est au format que nous attendions, afin que les attaques par injection SQL peuvent être évités dans une large mesure.
Par exemple, dans notre exemple précédent d'acceptation du paramètre de nom d'utilisateur, notre conception de produit devrait avoir une règle de nom d'utilisateur au début de l'enregistrement de l'utilisateur, telle que 5 à 20 caractères, qui ne peuvent être composés que de lettres majuscules et minuscules, de chiffres et de certains caractères Symboles, pas de caractères spéciaux. À ce stade, nous devrions avoir une fonction check_username pour effectuer une vérification unifiée. Cependant, il existe encore de nombreuses exceptions qui ne peuvent pas être appliquées à cette règle, telles que les systèmes de publication d'articles, les systèmes de commentaires et d'autres scénarios qui doivent permettre aux utilisateurs de soumettre des chaînes arbitraires, ce qui nécessite d'autres solutions telles que le filtrage.

2. Filtrer les symboles spéciaux

Pour les variables dont le format fixe ne peut pas être déterminé, un filtrage ou un échappement de symboles spéciaux doit être effectué.

3. Liez les variables, utilisez des instructions précompilées  

Le pilote mysqli de MySQL prend en charge les instructions précompilées. Différents langages de programmation ont des méthodes pour utiliser les instructions précompilées.

En fait, l'utilisation d'instructions précompilées pour les variables liées est le meilleur moyen d'empêcher l'injection SQL. La sémantique des instructions SQL précompilées ne changera pas. Dans les instructions SQL, les variables sont représentées par des points d'interrogation ?, et les pirates, aussi puissants soient-ils, ne peut pas modifier la structure de l'instruction SQL

5. Qu'est-ce que la précompilation sql

1.1 : Qu'est-ce qu'une déclaration préparée ? 

Habituellement, notre SQL reçoit l'exécution finale et les retours dans la base de données peuvent être divisés en trois processus :

  1. Analyse lexicale et sémantique
  2. Optimiser l'instruction sql et faire un plan d'exécution
  3. Exécuter et renvoyer le résultat

Nous appelons cette déclaration commune des déclarations immédiates.  

Mais dans de nombreux cas, l'une de nos instructions SQL peut être exécutée de manière répétée, ou seules des valeurs individuelles sont différentes à chaque fois qu'elle est exécutée (par exemple, la valeur de la clause where de la requête est différente, la valeur de la clause set de la mise à jour est différente, et les valeurs de l'insert sont différentes).
Si vous devez passer par l'analyse lexicale et sémantique ci-dessus, l'optimisation des instructions, la formulation du plan d'exécution, etc. à chaque fois, l'efficacité ne fonctionnera évidemment pas.

L'instruction dite précompilée consiste à remplacer la valeur dans ce type d'instruction par un espace réservé, qui peut être considéré comme une instruction SQL modélisée ou paramétrée. Généralement, ce type d'instruction est appelé instructions préparées ou instructions paramétrées. L'avantage des instructions précompilées est qu'ils peuvent être résumés
comme suit : compiler une fois et exécuter plusieurs fois, éliminant ainsi le besoin d'analyse et d'optimisation ; de plus, les instructions précompilées peuvent empêcher l'injection SQL.
Bien sûr, en termes d'optimisation, dans de nombreux cas, le plan d'exécution optimal ne peut pas être déterminé uniquement en connaissant le modèle de l'instruction SQL, mais doit souvent estimer le coût via des valeurs spécifiques.

1.2 : Fonction de précompilation MySQL

Notez que l'ancienne version de MySQL (avant 4.1) ne prend pas en charge la précompilation côté serveur, mais sur la base de la situation générale actuelle dans l'environnement de production de l'industrie, on peut fondamentalement considérer que MySQL prend en charge la précompilation côté serveur.

Jetons un coup d'œil à l'utilisation des instructions préparées dans MySQL.
(1) Construire une table Nous avons d'abord une table de test t dont la structure est la suivante :

mysql> show create table t\G
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE `t` (
  `a` int(11) DEFAULT NULL,
  `b` varchar(20) DEFAULT NULL,
  UNIQUE KEY `ab` (`a`,`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

(2) compiler

Nous  PREPARE stmt_name FROM preparable_stmallons précompiler une instruction sql avec la syntaxe que nous passerons ensuite

mysql> prepare ins from 'insert into t select ?,?';
Query OK, 0 rows affected (0.00 sec)
Statement prepared

(3) Exécution

La syntaxe que nous passons EXECUTE stmt_name [USING @var_name [, @var_name] ...]pour exécuter les instructions préparées

mysql> set @a=999,@b='hello';
Query OK, 0 rows affected (0.00 sec)
 
mysql> execute ins using @a,@b;
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0
 
mysql> select * from t;
+------+-------+
| a    | b     |
+------+-------+
|  999 | hello |
+------+-------+
1 row in set (0.00 sec)

Comme vous pouvez le voir, les données ont été insérées avec succès dans la table.

La portée des instructions précompilées dans MySQL est au niveau de la session, mais nous pouvons utiliser la variable max_prepared_stmt_count pour contrôler le maximum global d'instructions précompilées stockées.

mysql> set @@global.max_prepared_stmt_count=1;
Query OK, 0 rows affected (0.00 sec)
 
mysql> prepare sel from 'select * from t';
ERROR 1461 (42000): Can't create more than max_prepared_stmt_count statements (current value: 1)

Lorsque le nombre d'entrées précompilées a atteint le seuil, vous pouvez voir que MySQL signalera l'erreur indiquée ci-dessus.

(4) Release
Si nous voulons publier une instruction précompilée, nous pouvons utiliser {DEALLOCATE | DROP} PREPARE stmt_namela syntaxe pour le faire :

mysql> deallocate prepare ins;
Query OK, 0 rows affected (0.00 sec)

6. Pourquoi PrepareStatement peut empêcher l'injection SQL

Le principe est d'utiliser la méthode de pré-compilation, de compiler d'abord le jeu de paramètres dans l'instruction SQL qui peut être contrôlé par le client, de générer le jeu de variables temporaires correspondant, puis d'utiliser la méthode de réglage correspondante pour attribuer des valeurs aux éléments dans le jeu de variables temporaires. La fonction setString() effectuera une vérification de type obligatoire et une vérification de sécurité sur les paramètres entrants, de sorte que l'injection SQL peut être évitée. L'analyse spécifique suivante

(1): Pourquoi la déclaration sera injectée par sql

Parce que Statement est injecté par SQL car la structure de l'instruction SQL a changé. Par exemple:

"select*from tablename where username='"+uesrname+  
"'and password='"+password+"'"

La structure de l'instruction sql change après que l'utilisateur a saisi 'ou vrai ou'.

select*from tablename where username=''or true or'' and password=''

De cette façon, il n'est compté que lorsque le nom d'utilisateur et le mot de passe correspondent, mais après le changement, cela devient une relation logique OU, que le nom d'utilisateur et le mot de passe correspondent ou non, la valeur de retour de cette formule est toujours vrai ;

(2) Pourquoi la préparation peut empêcher l'injection SQL.

Parce que le style Préparation est

select*from tablename where username=? and password=?

L'instruction SQL sera pré-compilée avec la base de données avant d'obtenir l'entrée de l'utilisateur. De cette façon, quels que soient le nom d'utilisateur et le mot de passe saisis par l'utilisateur, le jugement est toujours une relation logique, empêchant l'injection SQL.

En bref, la raison pour laquelle le paramétrage peut empêcher l'injection est qu'une instruction est une instruction, qu'un paramètre est un paramètre et que la valeur d'un paramètre ne fait pas partie de l'instruction. La base de données s'exécute uniquement selon la sémantique de l'instruction Quant à savoir s'il faut courir avec un sac à dos normal ou un monstre, cela n'affectera pas l'itinéraire du voyage, ce n'est rien de plus que la différence entre courir plus vite et plus lentement.

7. Comment mybatis empêche l'injection SQL  

1. Examinez d'abord la différence entre les deux instructions SQL suivantes :

<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">
select id, username, password, role
from user
where username = #{username,jdbcType=VARCHAR}
and password = #{password,jdbcType=VARCHAR}
</select>
<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">
select id, username, password, role
from user
where username = ${username,jdbcType=VARCHAR}
and password = ${password,jdbcType=VARCHAR}
</select>

La différence entre # et $ dans mybatis :

1. # Traitez les données entrantes comme une chaîne et ajoutez des guillemets doubles aux données entrantes automatiquement.
Par exemple : où username=#{username}, si la valeur transmise est 111, alors la valeur lorsqu'elle est analysée dans sql est where username="111", si la valeur transmise est id, alors le sql analysé est où username = "identifiant". 

2. $ affiche et génère directement les données entrantes en sql.
Par exemple : où username=${username}, si la valeur transmise est 111, alors la valeur analysée dans sql est où username=111 ;
si la valeur transmise est ;drop table user;, alors le sql analysé est : sélectionnez l'identifiant, le nom d'utilisateur, le mot de passe, le rôle de l'utilisateur où nom d'utilisateur = ; supprimez l'utilisateur de la table ;

3. La méthode # peut empêcher l'injection SQL dans une large mesure, mais la méthode $ ne peut pas empêcher l'injection SQL.
4. La méthode $ est généralement utilisée pour importer des objets de base de données, tels que le nom de la table.
5. N'utilisez pas $ si vous pouvez généralement utiliser #. Si vous devez utiliser des paramètres tels que "${xxx}", vous devez manuellement les filtrer Travail, pour empêcher les attaques par injection sql.
6. Dans MyBatis, les paramètres au format "${xxx}" participeront directement à la compilation SQL, les attaques par injection ne pourront donc pas être évitées. Mais lorsqu'il s'agit de noms de tables dynamiques et de noms de colonnes, seul le format de paramètre "${xxx}" peut être utilisé. Par conséquent, ces paramètres doivent être traités manuellement dans le code pour empêcher l'injection.

[Conclusion] Lors de l'écriture de la déclaration de mappage de MyBatis, essayez d'utiliser le format "#{xxx}". Si vous devez utiliser des paramètres tels que "${xxx}", vous devez les filtrer manuellement pour éviter les attaques par injection SQL.

Comment mybatis empêche l'injection sql

En tant que framework de couche de persistance semi-automatisé, le framework MyBatis nous oblige à écrire manuellement ses instructions SQL.Bien sûr, il est nécessaire d'empêcher l'injection SQL à ce stade. En fait, MyBatis SQL est une fonction avec " entrée + sortie ", similaire à la structure de la fonction, reportez-vous aux deux exemples ci-dessus. Parmi eux, parameterType indique le type de paramètre d'entrée et resultType indique le type de paramètre de sortie. En réponse à ce qui précède, si nous voulons empêcher l'injection SQL, il est bien sûr nécessaire de travailler dur sur les paramètres d'entrée. La partie utilisant # dans le code ci-dessus est la partie où les paramètres d'entrée sont concaténés en SQL. Après avoir transmis les paramètres, imprimez l'instruction SQL exécutée et vous verrez que le SQL ressemble à ceci :

select id, username, password, role from user where username=? and password=?

Quels que soient les paramètres entrés, le SQL imprimé est comme ça. En effet, MyBatis a activé la fonction de précompilation. Avant l'exécution du SQL, le SQL ci-dessus sera envoyé à la base de données pour compilation ; lors de l'exécution, utilisez directement le SQL compilé et remplacez l'espace réservé " ?". Comme l'injection SQL ne peut fonctionner que sur le processus de compilation, cette méthode évite bien le problème de l'injection SQL.

[Principe d'implémentation sous-jacente] Comment MyBatis réalise la précompilation SQL ? En fait, en bas du framework, la classe PreparedStatement de JDBC est à l'œuvre. PreparedStatement est une sous-classe de Statement que nous connaissons bien. Ses objets contiennent des instructions SQL compilées. Cette approche "prête" peut non seulement améliorer la sécurité, mais également améliorer l'efficacité lorsque le même SQL est exécuté plusieurs fois. La raison en est que le SQL a été compilé et qu'il n'est pas nécessaire de le compiler à nouveau lors de sa réexécution

Je suppose que tu aimes

Origine blog.csdn.net/2301_77160226/article/details/130414616
conseillé
Classement