[RESOLU] Code antiinjection

Eléphant du PHP | 290 Messages

29 juil. 2014, 09:37

Bonjour,

Voici du code antiinjection SQL qui marche.
$nomutilisateur=$_POST['nomutilisateur'];
$motdepasse=$_POST['motdepasse'];
$motif1 = '/select/';
if (!preg_match($motif1, $nomutilisateur)){
} else { 
die();
}
Je souhaiterais pouvoir emêcher l'insertion d'autres mots du langage SQL comme le mot delete par exemple,
et empêcher de faire ça sur tous les champs de mon formulaire.
Je souhaiterais que ces mêmes mots en majuscules soient aussi reconnus comme "mauvais".
Je peux écrire le code suivant, mais c'est vraiment trop long:
$nomutilisateur=$_POST['nomutilisateur'];
$motdepasse=$_POST['motdepasse'];
$motif1 = '/select/';
if (!preg_match($motif1, $nomutilisateur)){
} else { 
die();
}
$nomutilisateur=$_POST['nomutilisateur'];
$motdepasse=$_POST['motdepasse'];
$motif2 = '/delete/';
if (!preg_match($motif2, $nomutilisateur)){
} else { 
die();
}
$nomutilisateur=$_POST['nomutilisateur'];
$motdepasse=$_POST['motdepasse'];
$motif1 = '/select/';
if (!preg_match($motif1, $motdepasse)){
} else { 
die();
}
$nomutilisateur=$_POST['nomutilisateur'];
$motdepasse=$_POST['motdepasse'];
$motif2 = '/delete/';
if (!preg_match($motif2, $motdepasse)){
} else { 
die();
}
$nomutilisateur=$_POST['nomutilisateur'];
$motdepasse=$_POST['motdepasse'];
$motif1 = '/SELECT/';
if (!preg_match($motif1, $nomutilisateur)){
} else { 
die();
}
$nomutilisateur=$_POST['nomutilisateur'];
$motdepasse=$_POST['motdepasse'];
$motif2 = '/DELETE/';
if (!preg_match($motif2, $nomutilisateur)){
} else { 
die();
}
$nomutilisateur=$_POST['nomutilisateur'];
$motdepasse=$_POST['motdepasse'];
$motif1 = '/SELECT/';
if (!preg_match($motif1, $motdepasse)){
} else { 
die();
}
$nomutilisateur=$_POST['nomutilisateur'];
$motdepasse=$_POST['motdepasse'];
$motif2 = '/DELETE/';
if (!preg_match($motif2, $motdepasse)){
} else { 
die();
}
Il y a en plus bien sûr plein d'autres mots du langage SQL que j'aimerais localiser
et d'autres champs de saisie, donc comme vous le voyez, avec mon code
demain on y est encore...
J'ai l'impression qu'il faut utiliser un array mais je ne sais pas
comment faire.
Est-ce que quelqu'un pourrait 1) m'aider à simplifier mon code?
Egalement, je souhaiterais empêcher toute intrusion de SQL
par le champs de la barre d'adresse du navigateur.
Pourriez-vous aussi m'aider 2) à faire cela?
Je n'ai aucune idée comment faire cela.

ViPHP
xTG
ViPHP | 7331 Messages

29 juil. 2014, 13:01

Quel est l'intérêt ?
Le but de l'anti-injection n'est pas d'interdire les mots clés mais d'interdire la possibilité qu'ils soient interprétés.

Du coup pour résoudre cela :
mysql_real_escape_string()
mysqli_real_escape_string()
PDO::quote()
Ou des requêtes préparées.

Eléphant du PHP | 290 Messages

29 juil. 2014, 13:27

Le but de ce travail est de me protéger contre des utilisateurs mal intentionnés.

J'utilise déjà les expressions rationnelles pour limiter le nombre de caractères autorisés,
ce qui revient à interdire tous les autres.
Ainsi par exemple, pas possible d'utiliser les chevrons < et > qui servent à écrire des balises.

Pour aller plus loin, je veux interdire aux utilisateurs l'usage de mots SQL comme select, delete, from,...
1) dans les champs de formulaire et 2) dans la barre d'adresse.
mysqli_real_escape_string ajoute un antislash devant certains caractères pour permettre
leur lecture littérale et empêcher leur interpétation mais pas pour interdire l'usage de mots appartenant au langage SQL.

Je ne suis pas un spécialiste du piratage, mais je trouve ça préférable que personne
ne puisse entrer nul part de mots SQL.

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

29 juil. 2014, 14:03

Hello !

Comme évoqué par xTG, le problème de l'injection est l’interprétation de codes malicieux. Il est effectivement souvent préférable et plus agréable pour l'utilisateur final de ne pas être limité par des contraintes qu'il ne comprends pas.

En effet, trop de rigueur dans tes contrôles risquent également de te pénaliser. Par exemple, si tu veux interdire la chaine "FROM" qui est un mot cél SQL, tu vas implicitement interdire également tous les autres mots qui contiennent cette chaine (fromage, réforme, ...). Il est donc souvent préférable de juste empêcher l'interprétation du SQL (avec les fonction spécifiée plus haut) et du HTML/JS en l'affichant sous forme de texte avec htmlentities() et cie.


Après pour répondre dans ton contexte, tu peux utiliser l'options "i" des expressions régulières pour gérer la casse et ne pas faire de différence entre majuscule/minuscule. Et pour simplifier l'écriture du code, tu peux faire un tableau des mots clés / chaines interdites et boucler sur celui-ci pour tester chaque valeur
$pattern_array = array('select', 'delete', ...);
foreach ($pattern in $pattern_array) {
  if (preg_match('/' . $pattern . '/i', $page)) { // traitement de l'erreur
    ...
  } 
}
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Eléphant du PHP | 290 Messages

29 juil. 2014, 14:40

Merci pour toutes ces précisions.
C'est très intéressant.
Je vais essayer ce que tu m'as donné (un nom d'utilisateur avec delete c'est plus rare).
Est-ce que je pourrais poser deux autres questions:

1) Comment se protéger contre du code malveillant inséré dans la barre d'adresse du navigateur?

2) Au final, pour un mot de passe, vaut-il mieux selon vous permettre d'utiliser uniquement les chiffres
et les lettres ou aussi d'autres signes comme ? ! % ou encore $, qui rendent plus complexe
la fabrication de combinaisons par des robots et rendent le nombre de combinaisons
possible bien plus important mais d'un autre côté ont un sens en programmation?
(! pour exprimer la négation, $ pour annoncer une variable,...)

Avatar du membre
Modérateur PHPfrance
Modérateur PHPfrance | 10684 Messages

29 juil. 2014, 15:47

Les données transmises via la barre d'adresse du navigateur sont les données transmises en "GET". Les formulaires te permettent de transmettre des données en GET (donc transmises via la barre d'adresse) ou en POST en fonction de l'attribut "method" du formulaire.

Le traitement de contrôle à apporter aux variables est le même quelque soit la méthode utilisée. D'une manière générale il ne faut jamais faire confiance aux données transmises par un utilisateur ;)


Concernant les mots de passe, pour ma part j'ai horreur des sites qui me contraignent à devoir adapter un mot de passe car ils n'acceptent pas les caractères de celui que je tente d'utiliser (au delà bien sur des contraintes de sécurité de taille, de complexité, ...).

En général, les bonnes pratiques en matière de mot de passe consistent à demander à ce que celui-ci :
- fasse au moins 8 caractères
- comporte au moins 3 des 4 types de caractères suivants : une majuscule, une minuscule, un chiffre ou un caractère spécial.

Il est donc usuel d'accepter les caractères spéciaux dans les mots de passes. D'autant plus que toujours pour des questions de sécurité, on va avoir tendance à chiffrer le mot de passe envoyé par l'utilisateur (sha(), md5(), ...) pour ne pas conserver celui-ci en clair dans la base de données (on effectue ensuite un chiffrement à chaque mot de passe saisi pour comparer les deux valeurs encodées). Ainsi quelque soient les valeurs des mots de passe tu stockeras et manipuleras de toute façon sur une chaine différente :)
Ce n'est pas en améliorant la bougie que l'on a inventé l'ampoule...

Eléphant du PHP | 290 Messages

29 juil. 2014, 18:32

Merci pour tout Ryle.
Ca me donne du travail :D

Je peux aussi te demander un truc, j'ai réussi à faire le programme qui refuse
du code SQL. Ca marche.
Pour l'instant ça ressemble à ça:
$pattern_array = array('select','delete','update');
          foreach($pattern_array as $pattern){
if (!preg_match('/' . $pattern . '/i', $pattern_array)) {
} else {
die(); 
}
}
Je souhaiterais maintenant appliquer ce programme à tous les champs.
J'essaye avec une boucle for (ça m'intéresse de travailler avec une boucle for :D
mais je n'y arrive pas :cry: )
Je ne sais pas si tu peux m'aider?
$pattern_array = array('select','delete','update');
$champs_array = array($nomutilisateur, $motdepasse, $reponsecaptcha);
$i=$champs_array;
          foreach($pattern_array as $pattern){
for ($i=0; $i<=2; $i++) 
if (!preg_match('/' . $pattern . '/i', $i)) {
} else {
die(); 
}
}

Eléphant du PHP | 290 Messages

29 juil. 2014, 18:34

Ca c'est ce que j'ai essayé de faire.

ViPHP
xTG
ViPHP | 7331 Messages

29 juil. 2014, 18:48

C'est une double boucle imbriquée du coup que tu souhaites faire.
Note : pour ton souci cela vient du fait que tu réécrases $i.
$pattern_array = array('select','delete','update');
$champs_array = array($nomutilisateur, $motdepasse, $reponsecaptcha);
foreach($pattern_array as $pattern){
   foreach($champs_array as $champs){
      if (!preg_match('/' . $pattern . '/i', $champs)) {

      } else {
         die();
   }
}

Eléphant du PHP | 290 Messages

29 juil. 2014, 19:48

Merci xTG, merci Ryle
C'est vraiment très sympa!! :D
Vous m'avez beaucoup aidé
Je vous en suis très reconnaissant :D