Quelles sont les meilleures fonctions de nettoyage des entrées PHP?

161

J'essaie de trouver une fonction par laquelle je peux passer toutes mes cordes pour désinfecter. Pour que la chaîne qui en sort soit sûre pour l'insertion de la base de données. Mais il y a tellement de fonctions de filtrage là-bas que je ne suis pas sûr de celles dont je devrais utiliser / avoir besoin.

S'il vous plaît, aidez-moi à remplir les blancs:

function filterThis($string) {
    $string = mysql_real_escape_string($string);
    $string = htmlentities($string);
    etc...
    return $string;
}
Lauren
la source
4
pour l'insertion, il est bon de simplement nettoyer contre l'injection SQL en utilisant mysql_real_escape_string. C'est lorsque vous utilisez les données SELECTed (en sortie html ou dans une formule / fonction php) que vous devez appliquer htmlentities
davidosomething
Voir stackoverflow.com/questions/60174/… pour une réponse spécifique au nettoyage pour l'insertion de base de données (il donne un exemple de PDO, que d'autres ont mentionné ci-dessous).
Pat

Réponses:

433

Arrêtez!

Vous faites une erreur ici. Oh, non, vous avez choisi les bonnes fonctions PHP pour rendre vos données un peu plus sûres. C'est très bien. Votre erreur est dans l' ordre des opérations , et comment et où utiliser ces fonctions.

Il est important de comprendre la différence entre le nettoyage et la validation des données utilisateur, l'échappement des données pour le stockage et l'échappement des données pour la présentation.

Désinfection et validation des données utilisateur

Lorsque les utilisateurs soumettent des données, vous devez vous assurer qu'ils ont fourni ce que vous attendez.

Désinfection et filtrage

Par exemple, si vous attendez un nombre, assurez-vous que les données soumises sont un nombre . Vous pouvez également convertir des données utilisateur dans d'autres types. Tout ce qui est soumis est initialement traité comme une chaîne, donc forcer les données numériques connues à être un entier ou un flottant rend le nettoyage rapide et indolore.

Qu'en est-il des champs de texte et des zones de texte de forme libre? Vous devez vous assurer qu'il n'y a rien d'inattendu dans ces domaines. Principalement, vous devez vous assurer que les champs qui ne devraient pas avoir de contenu HTML ne contiennent pas réellement de HTML. Il existe deux manières de résoudre ce problème.

Tout d'abord, vous pouvez essayer d' échapper à l' entrée HTML avec htmlspecialchars. Vous ne devez pas utiliser htmlentitiespour neutraliser le HTML, car il effectuera également le codage des caractères accentués et autres qui, selon lui, doivent également être codés.

Deuxièmement, vous pouvez essayer de supprimer tout HTML possible. strip_tagsest rapide et facile, mais aussi bâclé. HTML Purifier fait un travail beaucoup plus approfondi en supprimant tout le HTML et en permettant une liste blanche sélective de balises et d'attributs.

Les versions PHP modernes sont livrées avec l'extension de filtre , qui fournit un moyen complet de nettoyer les entrées utilisateur.

Validation

S'assurer que les données soumises sont exemptes de contenu inattendu ne représente que la moitié du travail. Vous devez également essayer de vous assurer que les données soumises contiennent des valeurs avec lesquelles vous pouvez réellement travailler.

Si vous attendez un nombre entre 1 et 10, vous devez vérifier cette valeur. Si vous utilisez l'une de ces nouvelles entrées numériques sophistiquées de l'ère HTML5 avec un spinner et des étapes, assurez-vous que les données soumises correspondent à l'étape.

Si ces données proviennent de ce qui devrait être un menu déroulant, assurez-vous que la valeur soumise est celle qui apparaît dans le menu.

Qu'en est-il des entrées de texte qui répondent à d'autres besoins? Par exemple, les entrées de date doivent être validées via strtotimela classe DateTime ou . La date donnée doit être comprise entre les plages attendues. Qu'en est-il des adresses e-mail? L' extension de filtre mentionnée précédemment peut vérifier qu'une adresse est bien formée, bien que je sois fan de la bibliothèque is_email .

La même chose est vraie pour tous les autres contrôles de formulaire. Vous avez des boutons radio? Validez par rapport à la liste. Avez-vous des cases à cocher? Validez par rapport à la liste. Vous avez un téléchargement de fichier? Assurez-vous que le fichier est d'un type attendu et traitez le nom de fichier comme des données utilisateur non filtrées.

Chaque navigateur moderne est livré avec un ensemble complet d'outils de développement intégrés, ce qui rend facile pour quiconque de manipuler votre formulaire. Votre code doit supposer que l'utilisateur a complètement supprimé toutes les restrictions côté client sur le contenu du formulaire !

Échapper aux données pour le stockage

Maintenant que vous vous êtes assuré que vos données sont au format attendu et ne contiennent que les valeurs attendues, vous devez vous soucier de la conservation de ces données dans le stockage.

Chaque mécanisme de stockage de données a une manière spécifique de s'assurer que les données sont correctement échappées et codées. Si vous créez du SQL, le moyen accepté de transmettre des données dans les requêtes consiste à utiliser des instructions préparées avec des espaces réservés .

L' extension PDO est l'un des meilleurs moyens de travailler avec la plupart des bases de données SQL en PHP . Il suit le modèle courant de préparation d'une instruction , de liaison de variables à l'instruction , puis d' envoi de l'instruction et des variables au serveur . Si vous n'avez jamais travaillé avec PDO, voici un très bon tutoriel orienté MySQL .

Certaines bases de données SQL ont leurs propres extensions spécialisées en PHP, notamment SQL Server , PostgreSQL et SQLite 3 . Chacune de ces extensions a préparé un support d'instructions qui fonctionne de la même manière préparer-lier-exécuter que PDO. Parfois, vous devrez peut-être utiliser ces extensions au lieu de PDO pour prendre en charge des fonctionnalités ou un comportement non standard.

MySQL a également ses propres extensions PHP. Deux d'entre eux, en fait. Vous ne voulez jamais utiliser que celui appelé mysqli . L'ancienne extension "mysql" est obsolète et n'est ni sûre ni sensée à utiliser à l'ère moderne.

Personnellement, je ne suis pas fan de mysqli. La manière dont il effectue la liaison de variables sur les instructions préparées est inflexible et peut être pénible à utiliser. En cas de doute, utilisez plutôt PDO.

Si vous n'utilisez pas de base de données SQL pour stocker vos données, consultez la documentation de l'interface de base de données que vous utilisez pour déterminer comment transmettre les données en toute sécurité.

Lorsque cela est possible, assurez-vous que votre base de données stocke vos données dans un format approprié. Stockez les nombres dans des champs numériques. Stockez les dates dans les champs de date. Stockez de l'argent dans un champ décimal, pas dans un champ à virgule flottante. Consultez la documentation fournie par votre base de données pour savoir comment stocker correctement différents types de données.

Échapper aux données pour la présentation

Chaque fois que vous montrez des données aux utilisateurs, vous devez vous assurer que les données sont échappées en toute sécurité, sauf si vous savez qu'elles ne doivent pas être échappées.

Lorsque vous émettez du HTML, vous devez presque toujours transmettre les données fournies à l'origine par l'utilisateur htmlspecialchars. En fait, la seule fois où vous ne devriez pas faire cela, c'est lorsque vous savez que l'utilisateur a fourni du code HTML et que vous savez qu'il a déjà été nettoyé à l'aide d'une liste blanche.

Parfois, vous devez générer du Javascript en utilisant PHP. Javascript n'a pas les mêmes règles d'échappement que HTML! Un moyen sûr de fournir des valeurs fournies par l'utilisateur à Javascript via PHP est d'utiliser json_encode.

Et plus

La validation des données comporte de nombreuses autres nuances.

Par exemple, le codage des jeux de caractères peut être un énorme piège . Votre demande doit suivre les pratiques décrites dans « UTF-8 tout au long ». Des attaques hypothétiques peuvent se produire lorsque vous traitez des données de chaîne comme un jeu de caractères incorrect.

Plus tôt, j'ai mentionné les outils de débogage du navigateur. Ces outils peuvent également être utilisés pour manipuler les données des cookies. Les cookies doivent être traités comme des entrées d'utilisateur non fiables .

La validation et l'échappement des données ne sont qu'un aspect de la sécurité des applications Web. Vous devez vous informer des méthodologies d'attaque des applications Web afin de pouvoir construire des défenses contre elles.

Charles
la source
Et lorsque vous le spécifiez, assurez-vous qu'il figure dans la liste des encodages pris en charge.
Charles
3
Et n'utilisez pas du tout htmlentities, remplacez-le par htmlspecialchars dans le but de remplacer simplement <>, pas tous les caractères de son entité
Votre bon sens
6
Veillez simplement à ne pas appeler htmlspecialcharsdeux fois, car il en parle dans la partie "Lorsque les utilisateurs soumettent des données" et dans la partie "Lors de l'affichage des données".
Savageman
2
J'ai voté pour. La réponse la plus utile que j'ai lue à partir de nombreuses questions / réponses concernant l'injection SQL.
akinuri
Absolument une réponse de qualité avec de nombreuses explications et liens permettant aux futurs utilisateurs d'explorer plus d'options. J'ai aussi une question de moi ...
James Walker
32

Le nettoyage le plus efficace pour empêcher l'injection SQL est le paramétrage à l'aide de PDO. À l'aide de requêtes paramétrées, la requête est séparée des données, ce qui supprime la menace d'une injection SQL de premier ordre.

En termes de suppression de HTML, strip_tagsc'est probablement la meilleure idée pour supprimer du HTML, car elle supprimera simplement tout. htmlentitiesfait ce que ça donne, donc ça marche aussi. Si vous devez analyser le code HTML à autoriser (c'est-à-dire que vous souhaitez autoriser certains balises), vous devez utiliser un analyseur existant mature tel que HTML Purifier

Derek H
la source
2
Oh mec, j'ai écrit ce mur géant de texte juste parce que je n'ai vu personne mentionner HTML Purifier, et ici vous m'avez battu de 40 minutes. ;)
Charles
3
Ne devriez-vous pas supprimer uniquement le HTML en sortie? OMI, vous ne devriez jamais changer les données d'entrée - vous ne savez jamais quand vous en aurez besoin
Joe Phillips
11

Entrée de base de données - Comment empêcher l'injection SQL

  1. Vérifiez que les données de type entier, par exemple, sont valides en vous assurant qu'il s'agit bien d'un entier
    • Dans le cas des non-chaînes, vous devez vous assurer que les données sont réellement du type correct
    • Dans le cas des chaînes, vous devez vous assurer que la chaîne est entourée de guillemets dans la requête (évidemment, sinon cela ne fonctionnerait même pas)
  2. Entrez la valeur dans la base de données tout en évitant l'injection SQL (mysql_real_escape_string ou requêtes paramétrées)
  3. Lors de la récupération de la valeur de la base de données, assurez-vous d'éviter les attaques de type Cross Site Scripting en vous assurant que le HTML ne peut pas être injecté dans la page (htmlspecialchars)

Vous devez échapper à l'entrée utilisateur avant de l'insérer ou de la mettre à jour dans la base de données. Voici une ancienne façon de le faire. Vous voudrez maintenant utiliser des requêtes paramétrées (probablement de la classe PDO).

$mysql['username'] = mysql_real_escape_string($clean['username']);
$sql = "SELECT * FROM userlist WHERE username = '{$mysql['username']}'";
$result = mysql_query($sql);

Sortie de la base de données - Comment empêcher XSS (Cross Site Scripting)

À utiliser htmlspecialchars()uniquement lors de la sortie des données de la base de données. Il en va de même pour HTML Purifier. Exemple:

$html['username'] = htmlspecialchars($clean['username'])

Et enfin ... ce que tu as demandé

Je dois souligner que si vous utilisez des objets PDO avec des requêtes paramétrées (la bonne façon de le faire), il n'y a vraiment pas de moyen facile d'y parvenir facilement. Mais si vous utilisez l'ancienne méthode «mysql», c'est ce dont vous auriez besoin.

function filterThis($string) {
    return mysql_real_escape_string($string);
}
Joe Phillips
la source
5

Mes 5 cents.

Personne ici ne comprend le mysql_real_escape_stringfonctionnement. Cette fonction ne filtre ni ne «désinfecte» rien.
Donc, vous ne pouvez pas utiliser cette fonction comme un filtre universel qui vous évitera l'injection.
Vous ne pouvez l'utiliser que lorsque vous comprenez comment fonctionne et où cela s'applique.

J'ai la réponse à la question très similaire que j'ai déjà écrite: en PHP, lorsque je soumets des chaînes à la base de données, dois-je m'occuper des caractères illégaux en utilisant htmlspecialchars () ou utiliser une expression régulière?
Veuillez cliquer pour obtenir l'explication complète de la sécurité côté base de données.

Quant aux entités HTML, Charles a raison de vous dire de séparer ces fonctions.
Imaginez simplement que vous allez insérer une donnée, générée par l'administrateur, qui est autorisé à publier du HTML. votre fonction va le gâcher.

Bien que je déconseille les entités html. Cette fonction est devenue obsolète depuis longtemps. Si vous voulez remplacer uniquement <, >et des "caractères dans un souci de sécurité HTML - utiliser la fonction qui a été intentionnellement développé à cette fin - un htmlspecialchars () un.

Votre bon sens
la source
1
mysql_real_escape_stringéchappe les caractères nécessaires à l'intérieur d'une chaîne. Il ne s'agit pas strictement de filtrer ou de désinfecter, mais d'inclure une chaîne entre guillemets non plus (et tout le monde le fait, je n'ai pratiquement jamais vu de question à ce sujet). Donc rien n'est nettoyé lorsque nous écrivons SQL? Bien sûr que non. Ce qui empêche l'injection SQL, c'est l'utilisation de mysql_real_escape_string. Aussi les guillemets, mais tout le monde le fait, et si vous testez ce que vous faites, vous vous retrouvez avec une erreur de syntaxe SQL avec cette omission. La vraie partie dangereuse est gérée mysql_real_escape_string.
Savageman
@Savageman désolé mon pote, tu ne comprends rien. Vous ne comprenez pas le fonctionnement de mysql_real_escape_string. Ces "caractères nécessaires" SONT des guillemets. Ni cette fonction ni les guillemets seuls ne nettoient quoi que ce soit. Ces 2 choses fonctionnent uniquement ensemble . Rendre la chaîne de requête juste syntaxiquement correcte, pas «à l'abri de l'injection». Et quelle erreur de syntaxe j'obtiendrais juste WHERE id = 1? ;)
Votre bon sens
Essayer WHERE my_field = two words (sans guillemets) pour obtenir l'erreur de syntaxe. Votre exemple est mauvais car il n'a pas besoin de guillemets ni d'échappements, juste une vérification numérique. De plus, je n'ai pas dit que les citations étaient inutiles. J'ai dit que tout le monde les utilise donc ce n'est pas la source de problèmes concernant l'injection SQL.
Savageman
1
@Savageman donc, que j'ai dit: Vous ne pouvez l'utiliser que lorsque vous comprenez comment cela fonctionne et où cela s'applique. Vous venez d'admettre que mysql_real_escape_string n'est pas applicable partout. Quant à everyone use themvous pouvez vérifier les codes ici sur SO. Beaucoup de gens n'utilisent pas de guillemets avec des nombres. Allez comprendre. Veuillez garder à l'esprit que je ne discute pas ici de ce que vous avez dit et de ce que vous ne faites pas. Je suis juste expliquer les règles de base de sécurité de base de données. Vous feriez mieux d'apprendre au lieu d'argumenter vide. Personne n'a mentionné de citations ou de casting ici mais m_r_e_s seulement comme si c'était magique. De quoi je parle
Votre bon sens
1
un, ainsi que @Charles. En tant que novice, l'interaction avec la base de données ... rendre les choses sûres pour la saisie et l'affichage, les caractères spéciaux, les problèmes d'injection, a été une courbe d'apprentissage très abrupte. Lire votre message et le sien (ainsi que vos autres réponses PHP à d'autres questions, m'a beaucoup aidé. Tx pour toutes vos contributions.
James Walker
2

Pour l'insertion de base de données, tout ce dont vous avez besoin est mysql_real_escape_string(ou utilisez des requêtes paramétrées). Vous ne souhaitez généralement pas modifier les données avant de les enregistrer, ce qui se passerait si vous les utilisiez htmlentities. Cela conduirait à un désordre brouillé plus tard lorsque vous le reliriez htmlentitiespour l'afficher quelque part sur une page Web.

À utiliser htmlentitieslorsque vous affichez les données sur une page Web quelque part.

Un peu lié, si vous envoyez des données soumises quelque part dans un e-mail, comme avec un formulaire de contact par exemple, assurez-vous de supprimer les retours à la ligne de toutes les données qui seront utilisées dans l'en-tête (comme le De: nom et adresse e-mail, sujet, etc. )

$input = preg_replace('/\s+/', ' ', $input);

Si vous ne le faites pas, ce n'est qu'une question de temps avant que les robots de spam ne trouvent votre formulaire et en abusent, j'ai appris à la dure.

Rob
la source
2

Cela dépend du type de données que vous utilisez. La meilleure mysqli_real_escape_stringsolution générale à utiliser serait , mais, par exemple, vous savez qu'il n'y aura pas de contenu HTML, l'utilisation de strip_tags ajoutera une sécurité supplémentaire.

Vous pouvez également supprimer des caractères dont vous savez qu'ils ne devraient pas être autorisés.

Aaron Harun
la source
1

Je recommande toujours d'utiliser un petit package de validation comme GUMP: https://github.com/Wixel/GUMP

Construisez toutes vos fonctions de base autour d'une bibliothèque comme celle-ci et il est presque impossible d'oublier l'assainissement. "mysql_real_escape_string" n'est pas la meilleure alternative pour un bon filtrage (comme l'explique "Your Common Sense") - et si vous oubliez de l'utiliser une seule fois, tout votre système sera attaquable par des injections et d'autres agressions désagréables.

Simon Schneider
la source
1

Pour tous ceux qui parlent ici et qui s'appuient sur mysql_real_escape_string, vous devez remarquer que cette fonction était obsolète sur PHP5 et n'existe plus sur PHP7.

À mon humble avis, la meilleure façon d'accomplir cette tâche est d'utiliser des requêtes paramétrées via l'utilisation de PDO pour interagir avec la base de données. Vérifiez ceci: https://phpdelusions.net/pdo_examples/select

Utilisez toujours des filtres pour traiter les entrées utilisateur. Voir http://php.net/manual/es/function.filter-input.php

Kuntur
la source
Cela ne répond pas réellement à la question. Pensez à modifier votre réponse pour inclure une solution.
kris
J'espère que vous aimez!
Kuntur
Je fais. Bonne réponse!
kris
Je suggère de noter que PHP 7 mysqli_real_escape_string()est disponible.
Chris
Bonjour Chris, les solutions exposées ici faisaient référence à mysql_real_escape_string, j'ai remarqué qui a lu désormais qu'il n'existe plus sur PHP7 et a proposé une alternative utilisant PDO (et des filtres) et non mysqli. N'hésitez pas à ajouter une note expliquant une solution en utilisant ce que vous suggérez. Regards
Kuntur
0

Vous utilisez mysql_real_escape_string () dans un code similaire au suivant.

$query = sprintf("SELECT * FROM users WHERE user='%s' AND password='%s'",
  mysql_real_escape_string($user),
  mysql_real_escape_string($password)
);

Comme le dit la documentation, son but est d'échapper les caractères spéciaux dans la chaîne passée en argument, en tenant compte du jeu de caractères actuel de la connexion afin qu'il soit sûr de le placer dans un mysql_query () . La documentation ajoute également:

Si des données binaires doivent être insérées, cette fonction doit être utilisée.

htmlentities () est utilisé pour convertir certains caractères dans les entités, lorsque vous sortez une chaîne dans le contenu HTML.

kiamlaluno
la source
0

C'est l'une des façons dont je pratique actuellement,

  1. Implantez le csrf et le jeton de tentation de sel avec la demande à effectuer par l'utilisateur et validez-les tous ensemble à partir de la demande. Référez-vous ici
  2. assurez-vous de ne pas trop vous fier aux cookies côté client et assurez-vous de vous entraîner à utiliser des sessions côté serveur
  3. lors de l'analyse de données, assurez-vous d'accepter uniquement le type de données et la méthode de transfert (comme POST et GET)
  4. Assurez-vous d'utiliser SSL pour votre application / application Web
  5. Assurez-vous également de générer une demande de session de base de temps pour restreindre intentionnellement la demande de spam.
  6. Lorsque les données sont analysées sur le serveur, assurez-vous de valider que la demande doit être effectuée dans la méthode de données souhaitée, telle que json, html, etc., puis continuez
  7. échapper à tous les attributs illégaux de l'entrée en utilisant le type d'échappement ... tel que realescapestring.
  8. après cela, vérifiez uniquement le format propre du type de données que vous voulez de l'utilisateur.
    Exemple:
    - E-mail: vérifiez si l'entrée est au format d'e-mail valide
    - texte / chaîne: Vérifiez que l'entrée est uniquement au format texte (chaîne)
    - nombre: vérifiez que seul le format numérique est autorisé.
    - etc. Pelase se réfère à la bibliothèque de validation d'entrée php du portail php
    - Une fois validé, veuillez continuer en utilisant l'instruction SQL / PDO préparée.
    - Une fois terminé, assurez-vous de quitter et de mettre fin à la connexion
    - N'oubliez pas d'effacer la valeur de sortie une fois terminé.

C'est tout ce que je pense est suffisant pour une seconde de base. Cela devrait empêcher toute attaque majeure du pirate informatique.

Pour la sécurité côté serveur, vous voudrez peut-être définir dans votre apache / htaccess la limitation des accès et la prévention des robots ainsi que la prévention du routage. Il y a beaucoup à faire pour la sécurité côté serveur en plus de la sécurité du système côté serveur.

Vous pouvez apprendre et obtenir une copie de la sec à partir du niveau htaccess apache sec (rpactices communs)

Ahmad Anuar
la source
0
function sanitize($string,$dbmin,$dbmax){
$string = preg_replace('#[^a-z0-9]#i', '', $string); //useful for strict cleanse, alphanumeric here
$string = mysqli_real_escape_string($con, $string); //get ready for db
if(strlen($string) > $dbmax || strlen($string) < $dbmin){
    echo "reject_this"; exit();
    }
return $string;
}
stkmedia
la source
0

Et ça

$string = htmlspecialchars(strip_tags($_POST['example']));

ou ca

$string = htmlentities($_POST['example'], ENT_QUOTES, 'UTF-8');
jerryurenaa
la source