Étant donné un tableau d'ID, $galleries = array(1,2,5)
je veux avoir une requête SQL qui utilise les valeurs du tableau dans sa clause WHERE comme:
SELECT *
FROM galleries
WHERE id = /* values of array $galleries... eg. (1 || 2 || 5) */
Comment puis-je générer cette chaîne de requête à utiliser avec MySQL?
Réponses:
la source
$galleries
devrait être validé en entrée avant cette déclaration! Les instructions préparées ne peuvent pas gérer les tableaux AFAIK, donc si vous avez l'habitude de lier des variables, vous pouvez facilement rendre possible l'injection SQL ici.$galleries
a la valeur suivante:array('1); SELECT password FROM users;/*')
. Si vous ne nettoyez pas cela, la requête se liraSELECT * FROM galleries WHERE id IN (1); SELECT password FROM users;/*)
. Remplacez les noms de table et de colonne par quelque chose que vous avez dans votre base de données et essayez cette requête, consultez les résultats. Vous trouverez une liste de mots de passe comme résultat, plutôt qu'une liste de galeries. Selon la façon dont les données sont sorties, ou ce que le script fait avec un tableau de données inattendues, cela pourrait obtenir la sortie dans la vue publique ... aïe.$galleries
comme prévu dans la question et vous l'exploitez en utilisant la "vulnérabilité d'injection sql" susmentionnée. Si vous ne pouvez pas - vous me payez 200 USD. Et ça?Utilisation de PDO: [1]
Utilisation de MySQLi [2]
Explication:
Utilisez l'
IN()
opérateur SQL pour vérifier si une valeur existe dans une liste donnée.En général, cela ressemble à ceci:
Nous pouvons construire une expression à placer à l'intérieur
()
de notre tableau. Notez qu'il doit y avoir au moins une valeur entre parenthèses ou MySQL retournera une erreur; cela revient à s'assurer que notre tableau d'entrée a au moins une valeur. Pour éviter les attaques par injection SQL, générez d'abord un?
pour chaque élément d'entrée afin de créer une requête paramétrée. Ici, je suppose que le tableau contenant vos identifiants est appelé$ids
:Étant donné un tableau d'entrée de trois éléments
$select
ressemblera à:Notez à nouveau qu'il y a un
?
pour chaque élément dans le tableau d'entrée. Ensuite, nous utiliserons PDO ou MySQLi pour préparer et exécuter la requête comme indiqué ci-dessus.Utilisation de l'
IN()
opérateur avec des chaînesIl est facile de basculer entre chaînes et entiers en raison des paramètres liés. Pour PDO, aucun changement n'est requis; pour le changement MySQLi
str_repeat('i',
àstr_repeat('s',
si vous devez vérifier les chaînes.[1]: J'ai omis certaines erreurs de vérification de la brièveté. Vous devez vérifier les erreurs habituelles pour chaque méthode de base de données (ou définir votre pilote de base de données pour lever des exceptions).
[2]: Nécessite PHP 5.6 ou supérieur. Encore une fois, j'ai omis de vérifier la brièveté des erreurs.
la source
$statement->bind_param(str_repeat('i', count($ids)), ...$ids);
alors, le...
développe les identifiants d'un tableau en plusieurs paramètres. Si vous faites référence àexpr IN (value,...)
cela, cela signifie simplement qu'il peut y avoir plus de valeurs, par exempleWHERE id IN (1, 3, 4)
. Il doit juste y en avoir au moins un....
: wiki.php.net/rfc/argument_unpackingints:
cordes:
la source
En supposant que vous désinfectiez correctement vos entrées au préalable ...
Ensuite, ajustez simplement votre requête:
Donnez des valeurs appropriées en fonction de votre ensemble de données.
la source
Utilisation:
Une simple
for each
boucle fonctionnera.La manière de Flavius / AvatarKava est meilleure, mais assurez-vous qu'aucune des valeurs du tableau ne contient de virgules.
la source
Comme la réponse de Flavius Stef , vous pouvez utiliser
intval()
pour vous assurer que toutesid
sont des valeurs int:la source
Pour MySQLi avec une fonction d'échappement:
Pour AOP avec déclaration préparée:
la source
Nous devons prendre soin des vulnérabilités d' injection SQL et d'une condition vide . Je vais gérer les deux comme ci-dessous.
Pour un tableau numérique pur, utilisez la conversion de type appropriée à savoir
intval
oufloatval
oudoubleval
sur chaque élément. Pour les types de chaînemysqli_real_escape_string()
qui peuvent également être appliqués aux valeurs numériques si vous le souhaitez. MySQL autorise les nombres ainsi que les variantes de date sous forme de chaîne .Pour échapper de manière appropriée les valeurs avant de passer à la requête, créez une fonction similaire à:
Une telle fonction serait probablement déjà à votre disposition dans votre application, ou peut-être que vous en avez déjà créé une.
Désinfectez le tableau de chaînes comme:
Un tableau numérique peut être nettoyé en utilisant
intval
oufloatval
ou à ladoubleval
place comme approprié:Enfin, créez la condition de requête
ou
Étant donné que le tableau peut également être vide parfois, comme
$galleries = array();
nous devons donc noter queIN ()
cela ne permet pas une liste vide. On peut également utiliser à laOR
place, mais le problème demeure. Donc, la vérification ci-dessuscount($values)
, est d'assurer la même chose.Et ajoutez-le à la requête finale:
la source
$query = 'SELECT * FROM galleries WHERE ' . (count($gallaries) ? "id IN ('" . implode("', '", array_map('escape', $gallaries)) . "')" : 0);
Plus sûr.
la source
La bibliothèque SafeMySQL de Col. Shrapnel pour PHP fournit des espaces réservés avec indication de type dans ses requêtes paramétrées et inclut quelques espaces réservés pratiques pour travailler avec des tableaux. L'
?a
espace réservé développe un tableau en une liste de chaînes d'échappement séparées par des virgules *.Par exemple:
* Notez que puisque MySQL effectue une coercition de type automatique, peu importe que SafeMySQL convertisse les identifiants ci-dessus en chaînes - vous obtiendrez toujours le résultat correct.
la source
Nous pouvons utiliser cette clause "WHERE id IN" si nous filtrons correctement le tableau d'entrée. Quelque chose comme ça:
Comme l'exemple ci-dessous:
C'est à dire maintenant que vous devez utiliser en toute sécurité
$query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";
la source
Vous pouvez avoir une table
texts
(T_ID (int), T_TEXT (text))
et une tabletest
(id (int), var (varchar(255)))
Dans
insert into test values (1, '1,2,3') ;
ce qui suit, les lignes des textes de table seront affichées oùT_ID IN (1,2,3)
:De cette façon, vous pouvez gérer une relation de base de données n2m simple sans table supplémentaire et en utilisant uniquement SQL sans avoir besoin d'utiliser PHP ou un autre langage de programmation.
la source
Plus un exemple:
la source
Outre l'utilisation de la requête IN, vous avez deux options pour le faire car dans une requête IN, il existe un risque de vulnérabilité d'injection SQL. Vous pouvez utiliser le bouclage pour obtenir les données exactes que vous souhaitez ou vous pouvez utiliser la requête avec le cas OU
la source
Manière sûre sans AOP:
(array)$ids
Convertir une$ids
variable en tableauarray_map
Transformez toutes les valeurs du tableau en entiersarray_unique
Supprimer les valeurs répétéesarray_filter
Supprimer les valeurs nullesimplode
Joignez toutes les valeurs à la sélection INla source
Parce que la question d'origine concerne un tableau de nombres et que j'utilise un tableau de chaînes, je n'ai pas pu faire fonctionner les exemples donnés.
J'ai trouvé que chaque chaîne devait être encapsulée entre guillemets simples pour fonctionner avec la
IN()
fonction.Voici ma solution
Comme vous pouvez le voir, la première fonction enveloppe chaque variable de tableau
single quotes (\')
puis implose le tableau.REMARQUE:
$status
n'a pas de guillemets simples dans l'instruction SQL.Il existe probablement une meilleure façon d'ajouter les citations, mais cela fonctionne.
la source
$filter = "'" . implode("','",$status) . "'";
'
à l'intérieur de la chaîne? Injection SQL vulnérable. Utilisez PDO :: quote ou mysqli_real_escape_string.Voici la méthode que j'ai utilisée, en utilisant PDO avec des espaces réservés nommés pour d'autres données. Pour surmonter l'injection SQL, je filtre le tableau pour n'accepter que les valeurs qui sont des entiers et je rejette toutes les autres.
la source
Les méthodes de base pour empêcher l'injection SQL sont les suivantes:
L'utilisation de requêtes préparées et de requêtes paramétrées est considérée comme la meilleure pratique, mais si vous choisissez la méthode des caractères d'échappement, vous pouvez essayer mon exemple ci-dessous.
Vous pouvez générer les requêtes en utilisant
array_map
pour ajouter une citation unique à chacun des éléments dans$galleries
:La var $ sql générée sera:
la source