我已经搜索了不同的形式,但不明白如何解决这里首先发现的问题(第一个链接,第二个链接),但它给了我 eval 中的错误。我无法弄清楚如何在 foreach 循环中的代码中解决。
foreach($_POST as $key => $value) {
if(!strstr($key, 'removeFile')){
//initialize variables using eval
eval("$" . $key . " = '" . sanitize($value) . "';");
}
}
首先,我对你的代码有问题:
-
eval
非常非常少需要,而且非常危险,请谨慎使用。我已经在 PHP 中开发了 10 多年,从未真正遇到过需要eval
的情况。这次也不例外。评估不是必需的 - 您正在清理整个
$_POST
阵列。这很好,但是有一些特殊的功能可以做到这一点,例如:filter_input_array
,array_filter
等等......更不用说已经包含可靠请求验证组件的现成开源项目和框架。 - 始终检查函数的返回值,您似乎正在使用
strstr
,但要厌倦返回不同类型的函数(例如strstr
:如果未找到针,它返回false
,但如果在大海捞针字符串的开头找到针,则返回0
)。您的if
语句可能无法按预期工作。 - 您假设
sanitize($value)
值不包含任何单引号。为什么?因为如果他们这样做,你最终会在你的evad字符串中出现语法错误
。
尽管如此,您可以轻松地使用变量变量编写代码,并添加一个简单的检查,以不踩踏范围内的现有变量:
$sanitized = array_filter($_POST, 'sanitize');//call sanitize on all values
foreach ($sanitized as $key => $value)
{
if (!isset($$key) && strstr($key, 'removeFile') === false)
$$key = $value;
}
但实际上,$_POST
值属于一起,它们是请求的一部分,并且应该保持分组......要么在数组中,要么在某种对象中。不要将每个值分配给它自己的变量,因为很快你就会忘记哪些变量被设置,哪些变量没有被设置。使用 unset 变量会创建该变量,null
赋值,因此您现在拥有的代码非常容易出错:
//request 1: POST => id=123&foo=bar
foreach ($sanitized as $k => $v)
$$k = $v;
$query = 'SELECT x, y, z FROM tbl WHERE id = ?';//using posted ID as value
$stmt = $db->prepare($query);
$stmt->execute(array($id));
一切都很好,因为$id
设置了,但永远不要信任网络,不要假设,仅仅因为设置了$_POST
,所有的键都会被设置,并且它们的值是正确的:
//request 2: POST => foo=bar&page=2
foreach ($sanitized as $k => $v)
$$k = $v;
$query = 'SELECT x, y, z FROM tbl WHERE id = ?';//using posted ID as value
$stmt = $db->prepare($query);
$stmt->execute(array($id));//id is null
现在我们遇到了一个问题。这只是代码如何导致问题的一个示例。想象一下脚本增长一点,看看这个:
//request 3: POST => id=123&foo=bar&page=2
foreach ($sanitized as $k => $v)
$$k = $v;
//$id is 123, $foo is bar and $page = 2
$query = 'SELECT x, y, z FROM tbl WHERE id = ? LIMIT 10';//using posted ID as value
//a lot more code containing this statement:
$page = someFunc();
$log->write('someFunc returned log: '.$page);
//more code
$offset = 10*($page-1);//<-- page is not what we expected it to be
$query .= sprintf(' OFFSET %d', $offset);
$stmt = $db->prepare($query);
$stmt->execute(array($id));
现在这可能看起来很牵强,很愚蠢,但相信我:所有这些事情的发生,比我想知道的要多。添加一些意外覆盖进一步使用的现有变量的代码一直在发生。特别是在过程代码中。不要盲目地解压缩阵列。保留该单个变量,并使用键来避免:
- 白发
- 突然、剧烈的秃顶
- 失去理智
- 出血性溃疡
- 在工作环境中:灾难性的数据丢失
- 突然失业
- 。因为这样的代码会让独角兽哭泣,而兄弟会追捕你
作为您链接到的帖子的第一个答案,问题是当使用双引号时,PHP 认为您的eval()
代码以变量开头。由于情况并非如此,您有两种选择。使用单引号并记住转义在代码中声明字符串的单引号或转义美元符号。
奖金说明
对于您尝试解决的问题,存在更优雅的解决方案。我能想到的最佳解决方案是使用 extract
函数。这有两个主要好处。
- 它适用于所有关联数组
- 您可以指定不同的标志,以帮助您区分提取的变量并避免变量注入。
您可以使用的一个标志是 EXTR_PREFIX_ALL
.这将为所有提取的变量添加您自己的前缀。然后,您将访问前缀为"PREFIX_"的变量,如下所示:
$array = [
'variable1' => 'foo',
'variable2' => 'bar'
];
extract($array, EXTR_PREFIX_ALL, 'PREFIX_');
$value1 = $PREFIX_variable1; // Equals: foo
$value2 = $PREFIX_variable2; // Equals: bar
关于代码注入的一点
假设您有一些代码:
$login = false;
$login
变量确定用户是否已登录。然后在某个地方,您将"extract"函数与以下数组一起使用,而无需使用任何标志。
$array = [
'login' => true,
'foo' => 'bar'
];
extract($array);
现在,您的$login
变量将设置为 true
,发布数据的用户将覆盖初始设置,并在没有有效登录的情况下访问您的网站。请记住,这是一个过于简化的示例,但仍然有效。
为了克服这个问题,您可以使用标志EXTR_SKIP
或前缀,就像我之前展示的那样。如果已定义具有相同名称的变量,则 EXTR_SKIP
标志将跳过数组元素。所以现在你的代码不会覆盖你的$login
变量。
extract($array, EXTR_SKIP); // Skip existing variables
// Or
extract($array, EXTR_PREFIX_ALL, 'prefix'); // Prefix them all.
希望这可以指导您正确选择您的需求。
问候。