$galleries = array(1,2,5)
,我希望有一个SQL查询,该查询使用其WHERE子句中的数组值,例如: pre> 如何生成用于MySQL的查询字符串?
#1 楼
注意!该答案包含一个严重的SQL注入漏洞。不要使用此处提供的代码示例,而请确保未清除所有外部输入。
评论
标识符仍然是带引号的列表,因此例如以“ WHERE id IN('1,2,3,4')”形式出现。您需要分别为每个标识符加上引号,否则请将括号内的引号引起来。
–抢夺
09年5月25日在20:05
我只是添加警告:在此语句之前,应验证$ galleries的输入!预处理语句无法处理数组AFAIK,因此,如果您习惯于绑定变量,则可以在此处轻松地进行SQL注入。
– leemes
2012年3月27日上午11:07
对于像我这样使用PHP的新手,有人可以解释一下,也可以向我指向要解释的资源,这为什么易于注入,以及应该如何正确地进行预防?如果ID列表是在下一个查询运行之前立即从查询生成的,那仍然很危险吗?
– ministe2003
2014年9月9日下午13:52
@ ministe2003想象一下$ galleries是否具有以下值:array('1);从用户中选择密码; / *')。如果您不清除它,查询将显示为SELECT * FROM Galleries ID IN(1);选择来自用户的密码; / *)。将表和列名更改为数据库中的名称,然后尝试查询,并检查结果。您会找到一个密码列表,而不是图库列表。根据数据的输出方式或脚本对一系列意外数据的处理方式,可能会将其输出到公共视图中。
–克里斯·贝克(Chris Baker)
2014年10月2日15:19
对于所提问题,这是一个完全有效和安全的答案。谁抱怨这并不安全-我如何通过问题中提供的$ galleries设置此特定代码的交易,然后使用上述“ sql注入漏洞”来利用它。如果不能,请给我200美元。那个怎么样?
–zerkms
15年4月15日在21:36
#2 楼
使用PDO:[1]$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;
$statement = $pdo->prepare($select);
$statement->execute($ids);
使用MySQLi [2]
$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;
$statement = $mysqli->prepare($select);
$statement->bind_param(str_repeat('i', count($ids)), ...$ids);
$statement->execute();
$result = $statement->get_result();
解释:
使用SQL
IN()
运算符检查值是否存在通常看起来像这样:
expr IN (value,...)
我们可以构建一个表达式,将其放置在数组中的
()
内。请注意,括号内必须至少有一个值,否则MySQL将返回错误;这等于确保我们的输入数组至少具有一个值。为了帮助防止SQL注入攻击,首先为每个输入项生成一个?
来创建参数化查询。在这里,我假设包含您的ID的数组称为$ids
:输入数组中的每个项目都有一个$select
。然后,我们将使用PDO或MySQLi如上所述准备和执行查询。对于PDO,无需更改;对于MySQLi,如果需要检查字符串,请将?
更改为IN()
。[1]:为简洁起见,我省略了一些错误检查。您需要检查每种数据库方法的常见错误(或将数据库驱动程序设置为引发异常)。
[2]:需要PHP 5.6或更高版本。同样,为了简洁起见,我省略了一些错误检查。
评论
... $ ids有什么作用?我收到“语法错误,意外的'。'”。
–马塞尔
2014年9月18日下午0:43
我看到他们,我正在使用MySQLi,并且我有php 5.6
–马塞尔
2014-09-18 15:52
如果您指的是$ statement-> bind_param(str_repeat('i',count($ ids)),... $ ids);然后...将ID从数组扩展为多个参数。如果您指的是expr IN(值,...),则意味着可以有更多的值,例如WHERE id IN(1、3、4)。至少需要有一个。
–莱维·莫里森(Levi Morrison)
16年4月26日在19:27
我很困惑<<<是什么,但是找到了参考:php.net/manual/en/…
–赞加人
16年6月16日在4:23
另外,这里是...的参考:wiki.php.net/rfc/argument_unpacking
–赞加人
16年6月16日在4:35
#3 楼
整数:$query = "SELECT * FROM `$table` WHERE `$column` IN(".implode(',',$array).")";
字符串:
$query = "SELECT * FROM `$table` WHERE `$column` IN('".implode("','",$array)."')";
评论
为什么是“ \”?请告诉我
– zloctb
2013年9月20日上午11:42
#4 楼
假设您事先正确清理了输入内容...$matches = implode(',', $galleries);
然后只需调整您的查询即可:
SELECT *
FROM galleries
WHERE id IN ( $matches )
根据您的数据集适当引用值。
评论
我尝试了您的建议,但它只是获取了第一个键值。我知道这没有意义,但是如果我使用user542568示例进行操作,那么该死的事情就可以了。
–塞缪尔·拉姆赞(Samuel Ramzan)
4月25日18:16
#5 楼
使用:select id from galleries where id in (1, 2, 5);
一个简单的
for each
循环将起作用。包含逗号。#6 楼
作为Flavius Stef的答案,您可以使用intval()
来确保所有id
均为int值:$ids = join(',', array_map('intval', $galleries));
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
#7 楼
对于具有转义功能的MySQLi:$ids = array_map(function($a) use($mysqli) {
return is_string($a) ? "'".$mysqli->real_escape_string($a)."'" : $a;
}, $ids);
$ids = join(',', $ids);
$result = $mysqli->query("SELECT * FROM galleries WHERE id IN ($ids)");
对于具有预准备语句的PDO:
$qmarks = implode(',', array_fill(0, count($ids), '?'));
$sth = $dbh->prepare("SELECT * FROM galleries WHERE id IN ($qmarks)");
$sth->execute($ids);
评论
这很好,简短,可以避免代码插入漏洞! +1
–斯蒂芬·里希特(Stephan Richter)
17年11月8日在9:20
MySQLi也准备了语句。不要逃避您的输入,这可能仍然容易受到SQL注入的攻击。
–达尔曼
2月25日在22:54
#8 楼
我们应该注意SQL注入漏洞和空条件。我将按以下方式处理这两个问题。对于纯数字数组,请在每个元素上使用适当的类型转换,即
intval
或floatval
或doubleval
。对于字符串类型mysqli_real_escape_string()
,如果需要,也可以将其应用于数字值。 MySQL允许数字和日期变体作为字符串使用。 br />这样的功能很可能已经在您的应用程序中为您提供了,或者您已经创建了一个功能。 可以使用
intval
或floatval
或doubleval
代替数字数组来对数字数组进行清理: > function escape($string)
{
// Assuming $db is a link identifier returned by mysqli_connect() or mysqli_init()
return mysqli_real_escape_string($db, $string);
}
或
$values = array_map('escape', $gallaries);
由于有时数组也可以为空,例如
$galleries = array();
,因此我们应该注意不允许有一个空列表。也可以改用IN ()
,但问题仍然存在。因此,上述检查OR
可以确保相同。并将其添加到最终查询中:
$values = array_map('intval', $gallaries);
提示:如果要在空数组的情况下显示所有记录(不进行过滤),而不是隐藏所有行,只需在三元组的假部分中将0替换为1。
评论
为了使我的解决方案成为一线(和丑陋的),以防万一有人需要:$ query ='SELECT * FROM Galleries WHERE'。 (count($ gallaries)?“ id IN('”。implode(“','”,array_map('escape',$ gallaries))。“')”:0);
–伊扎尔·阿兹米(Izhar Aazmi)
15年4月17日在13:38
#9 楼
更安全。$galleries = array(1,2,5);
array_walk($galleries , 'intval');
$ids = implode(',', $galleries);
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
#10 楼
Col. Shrapnel的PHP SafeMySQL库在其参数化查询中提供了带类型提示的占位符,并包括几个方便的用于处理数组的占位符。?a
占位符将数组扩展为以逗号分隔的转义字符串列表*。例如:
$someArray = [1, 2, 5];
$galleries = $db->getAll("SELECT * FROM galleries WHERE id IN (?a)", $someArray);
*请注意,由于MySQL执行自动类型强制,SafeMySQL将上面的id转换为字符串没关系-您仍然会得到正确的结果。
#11 楼
如果我们正确过滤输入数组,则可以使用此“ WHERE id IN”子句。像这样的例子:$galleries = array();
foreach ($_REQUEST['gallery_id'] as $key => $val) {
$galleries[$key] = filter_var($val, FILTER_SANITIZE_NUMBER_INT);
}
像下面的例子:现在您应该安全使用
$query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";
评论
@ levi-morrison发布了很多更好的解决方案。
– Supratim Roy
15年4月18日在7:32
#12 楼
您可能具有表texts
和表(T_ID (int), T_TEXT (text))
test
在
(id (int), var (varchar(255)))
中,以下内容将从表文本输出行,其中insert into test values (1, '1,2,3') ;
:您可以管理简单的n2m数据库关系,而无需额外的表,而仅使用SQL,而无需使用PHP或其他某种编程语言。#13 楼
更多示例: $galleryIds = [1, '2', 'Vitruvian Man'];
$ids = array_filter($galleryIds, function($n){return (is_numeric($n));});
$ids = implode(', ', $ids);
$sql = "SELECT * FROM galleries WHERE id IN ({$ids})";
// output: 'SELECT * FROM galleries WHERE id IN (1, 2)'
$statement = $pdo->prepare($sql);
$statement->execute();
#14 楼
不使用PDO的安全方法: $ids = array_filter(array_unique(array_map('intval', (array)$ids)));
if ($ids) {
$query = 'SELECT * FROM `galleries` WHERE `id` IN ('.implode(',', $ids).');';
}
(array)$ids
Cast $ids
变量到数组array_map
将所有数组值转换为整数array_unique
删除重复值array_filter
删除零值implode
将所有值连接到IN选择#15 楼
除了使用IN查询外,您还有两种选择,因为在IN查询中存在SQL注入漏洞的风险。您可以使用循环获取所需的确切数据,也可以将查询与OR case一起使用1. SELECT *
FROM galleries WHERE id=1 or id=2 or id=5;
2. $ids = array(1, 2, 5);
foreach ($ids as $id) {
$data[] = SELECT *
FROM galleries WHERE id= $id;
}
#16 楼
因为原始问题与数字数组有关,并且我正在使用字符串数组,所以无法使给定的示例起作用。我发现每个字符串都需要封装在单引号中才能起作用使用
IN()
功能。 这是我的解决方案
foreach($status as $status_a) {
$status_sql[] = '\''.$status_a.'\'';
}
$status = implode(',',$status_sql);
$sql = mysql_query("SELECT * FROM table WHERE id IN ($status)");
如您所见,第一个函数将每个数组变量包装在
single quotes (\')
中,然后将其内爆。 /> 注意:
$status
在SQL语句中没有单引号。评论
或者$ filter =“'”。 implode(“','”,$ status)。 “'”;
–亚历杭德罗·萨拉曼卡·马祖埃洛(Alejandro Salamanca Mazuelo)
15年4月13日在20:06
这是易受攻击的。
– Mark Amery
15年5月17日在18:30
字符串转义在哪里?例如'字符串内? SQL注入易受攻击。使用PDO :: quote或mysqli_real_escape_string。
– 18C
17年12月21日在13:41
#17 楼
下面是我使用的方法,将PDO与命名占位符一起用于其他数据。为了克服SQL注入,我正在过滤数组以仅接受整数值并拒绝所有其他值。$owner_id = 123;
$galleries = array(1,2,5,'abc');
$good_galleries = array_filter($chapter_arr, 'is_numeric');
$sql = "SELECT * FROM galleries WHERE owner=:OWNER_ID AND id IN ($good_galleries)";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(
"OWNER_ID" => $owner_id,
));
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
#18 楼
防止SQL注入的基本方法是:使用准备好的语句和参数化查询
转义不安全变量中的特殊字符
使用准备好的语句和参数化查询是更好的做法,但是,如果选择转义字符方法,则可以在下面的示例中尝试。
可以使用
array_map
添加单引号来生成查询。到$galleries
中的每个元素:$galleries = array(1,2,5);
$galleries_str = implode(', ',
array_map(function(&$item){
return "'" .mysql_real_escape_string($item) . "'";
}, $galleries));
$sql = "SELECT * FROM gallery WHERE id IN (" . $galleries_str . ");";
生成的$ sql var将是:
SELECT * FROM gallery WHERE id IN ('1', '2', '5');
注意:mysql_real_escape_string(如其文档中所述)在PHP 5.5.0中已弃用,在PHP 7.0.0中已删除。相反,应使用MySQLi或PDO_MySQL扩展。另请参见MySQL:选择API指南和相关的FAQ,以获取更多信息。此功能的替代方法包括:
mysqli_real_escape_string()
PDO :: quote()
评论
与其他答案相比,这不仅没有添加任何新内容,而且尽管存在接受了多年的关于SQL注入的警告,但它容易受到注入的攻击。
– Mark Amery
15年5月17日在18:37
评论
可能链接的问题:stackoverflow.com/questions/6617620/…stackoverflow.com/questions/7538927/…stackoverflow.com/questions/5295714/array-in-sql-querystackoverflow.com/questions/8205816/…stackoverflow.com/问题/ 7298216 /…@trante这是最古老的(2009)。
是否有类似问题的解决方案,例如SELECT * FROM TABLE WHERE NAME LIKE('ABC%'或'ABD%'OR ..)