题
我最近正在阅读此主题最糟糕的PHP实践。
在第二个答案中,有一个关于 extract()
的使用的小型讨论,我只想知道所有的烦恼是什么。
我个人使用它来删除一个给定的数组,例如 $ _ GET
或 $ _ POST
,然后我在那里清理变量,因为它们已经为我方便地命名了。
这是不好的做法吗?这有什么风险?您对 extract()
的使用有何看法?
解决方案
我发现这只是一种不好的做法,因为它可能导致许多变量,未来的维护者(或者你自己在几周内)不知道他们来自哪里。请考虑以下情况:
extract($someArray); // could be 我发现这只是一种不好的做法,因为它可能导致许多变量,未来的维护者(或者你自己在几周内)不知道他们来自哪里。请考虑以下情况:
$a = $someLongNameOfTheVariableArrayIDidntWantToType;
$a['myVariable'];
$ someVariable
来自哪里?谁能说出来?
我没有看到从他们开始的数组中访问变量的问题,所以你真的需要使用 extract()>来提供的好例子代码>让我觉得它值得。如果您真的担心输入一些额外的字符,那么就这样做:
<*>
我认为这里关于安全方面的评论有些过分夸大了。该函数可以采用第二个参数,实际上可以很好地控制新创建的变量,包括不覆盖任何现有变量( EXTR_SKIP
),只覆盖现有变量(这样就可以创建白名单)( EXTR_IF_EXISTS
),或为变量添加前缀( EXTR_PREFIX_ALL
)。
POST or anything
/* snip a dozen or more lines */
echo $someVariable;
$ someVariable
来自哪里?谁能说出来?
我没有看到从他们开始的数组中访问变量的问题,所以你真的需要使用 extract()>来提供的好例子代码>让我觉得它值得。如果您真的担心输入一些额外的字符,那么就这样做:
我认为这里关于安全方面的评论有些过分夸大了。该函数可以采用第二个参数,实际上可以很好地控制新创建的变量,包括不覆盖任何现有变量( EXTR_SKIP
),只覆盖现有变量(这样就可以创建白名单)( EXTR_IF_EXISTS
),或为变量添加前缀( EXTR_PREFIX_ALL
)。
其他提示
现在来吧。人们责怪工具而不是用户。
这就像是在讨论 unlink()
,因为你可以用它删除文件。 extract()
是一个像任何其他函数一样的函数,明智且负责任地使用它。但不要声称它本身不好,那只是无知。
风险在于:不信任来自用户的数据,并提取到当前符号表意味着,您的变量可能会被用户提供的内容覆盖。
<?php
$systemCall = 'ls -lh';
$i = 0;
extract(风险在于:不信任来自用户的数据,并提取到当前符号表意味着,您的变量可能会被用户提供的内容覆盖。
yourscript.php?i=10&systemCall=rm%20-rf
(一个荒谬的例子)
但现在是猜测或知道代码调用的恶意用户:
yourscript.php?data[]=a&data[]=b&data[]=c
而不是
<*>
现在,$ systemCall和$ i被覆盖,导致您的脚本首先删除您的数据然后挂起。
GET);
system($systemCall);
do {
print_r($data[$i];
$i++;
} while ($i != 3);
?>
(一个荒谬的例子)
但现在是猜测或知道代码调用的恶意用户:
<*>而不是
<*>现在,$ systemCall和$ i被覆盖,导致您的脚本首先删除您的数据然后挂起。
它没有任何问题。否则就不会实施。当您向视图传递(赋值)变量时,许多(MVC)框架都会使用它。你只需要仔细使用它。在将这些数组传递给extract()之前清理它们并确保它不会覆盖您的变量。别忘了这个函数还接受了一些参数! 如果发生碰撞,使用第二个和第三个参数可以控制行为。您可以覆盖,跳过或添加前缀。 http://www.php.net/extract
如果不小心使用,可能会使你工作的其他人感到困惑:
<?php
$array = array('huh' => 'var_dump', 'whatThe' => 'It\'s tricky!', 'iDontGetIt' => 'This Extract Function');
extract($array);
$huh($whatThe, $iDontGetIt);
?>
收益率:
string(12) "It's tricky!"
string(21) "This Extract Function"
在混淆中使用会很有用。但我无法克服“变量来自何处?”我遇到的问题。
人们对提取物全面了解,因为它会滥用潜力。做任何类似提取($ _ POST)的事情在任何情况下都不是一个好主意,即使你知道你在做什么。但是,当您执行诸如将变量暴露给视图模板或类似的东西时,它确实具有它的用途。基本上,只有当你非常确定你有充分理由这样做时才使用它,并且如果你想要把像$ _POST这样疯狂的东西传递给它,那么就要理解如何使用extract类型参数。
我想很多人不推荐使用它的原因是提取 $ _ GET
和 $ _ POST
(甚至 $ _ REQUEST
)superglobals在全局命名空间中注册变量,其名称与这些数组中的每个键相同,基本上模拟REGISTER_GLOBALS = 1.
我会让 PHP手册进行讨论我
背景: extract($ _ REQUEST)
与在php.ini中设置 register_globals = On
相同
如果在函数中提取,则变量仅在该范围内可用。这通常用在视图中。简单的例子:
//View.php
class View {
function render($filename = null) {
if ($filename !== null) {
$this->filename = $filename;
}
unset($filename);
extract($this->variables);
ob_start();
$this->returned = include($this->dir . $this->filename);
return ob_get_clean();
}
}
//test.php
$view = new View;
$view->filename = 'test.phtml';
$view->dir = './';
$view->variables = array('test' => 'tset');
echo $view->render('test.phtml');
var_dump($view->returned);
//test.phtml
<p><?php echo $test; ?></p>
使用一些替代目录,检查文件是否存在以及定义的变量和方法 - 您几乎已经复制了Zend_View。
您还可以在include之后添加 $ this-&gt; outVariables = get_defined_vars(); 以运行具有特定变量的代码,并获取这些变量以使用旧的PHP代码。
只要您以安全的方式使用,提取物就是安全的。你想要做的是将数组的键过滤到你想要使用的键,如果你的场景需要它们,可以检查所有这些键是否存在。
#Extract only the specified keys.
$extract=array_intersect_key(
get_data()
,$keys=array_flip(['key1','key2','key3','key4','key5'])
);
#Make sure all the keys exist.
if ($missing=array_keys(array_diff_key($keys,$extract))) {
throw new Exception('Missing variables: '.implode(', ',$missing));
}
#Everything is good to go, you may proceed.
extract($extract);
或
#If you don't care to check that all keys exist, you could just do this.
extract(array_intersect_key(
get_data()
,array_flip(['key1','key2','key3','key4','key5'])
));
风险与register_globals相同。您可以让攻击者在脚本中设置变量,只需篡改请求即可。
永远不要在全局范围内提取($ _ GET)。除此之外,它有其用途,比如调用一个可能(可能)有很多可选参数的函数。
对于WordPress开发人员来说,这应该看起来很模糊:
function widget (Array $args = NULL)
{
extract($args);
if($before_widget) echo $before_widget;
// do the widget stuff
if($after_widget) echo $after_widget;
}
widget(array(
'before_widget' => '<div class="widget">',
'after_widget' => '</div>'
));
正如有人在另一个主题中提到的那样,这里是一种更安全的方式使用提取,只允许它提取您指定的变量,而不是数组包含的所有内容。
这有双重目的,即记录变量的来源,因此跟踪变量不会那么困难。
每种方法的使用都可能导致某些情况,它可能成为应用程序的失败点。 我个人觉得extract()不应该用于用户输入(不可预测)和未清理的数据。
即使CodeIgniter核心代码使用提取,因此如果数据被清理并处理得当,使用该方法一定不会有任何损害。
我使用了带有EXTR_IF_EXISTS开关的CodeIgniter模型中的extract并限制了变量的数量,它运行得很好。
请注意,如果您正在使用用户数据(如请求结果), extract()
是不安全的,因此最好将此函数与标志 EXTR_IF_EXISTS 代码>和
EXTR_PREFIX_ALL
。
如果使用正确,可以安全使用
稍微解释一下以前的答案......只要你正确地过滤输入(正如其他人已经说过的那样),extract()没有任何问题;否则你最终会遇到这样的巨大安全问题:
<?php
// http://foobar.doo?isLoggedIn=1
$isLoggedIn = (new AdminLogin())->isLoggedIn(); // Let's assume this returns FALSE
extract(稍微解释一下以前的答案......只要你正确地过滤输入(正如其他人已经说过的那样),extract()没有任何问题;否则你最终会遇到这样的巨大安全问题:
<*>GET);
if ($isLoggedIn) {
echo "Okay, Houston, we've had a problem here.";
} else {
echo "This is Houston. Say again, please.";
}
不再使用extract()的另一个好理由是PHP中有动力使用 HHVM 声称PHP的速度提高了约10倍。 Facebook(制作它)正在使用它,维基百科正在使用它,而传言WordPress正在研究它。
它仍然是一种阿尔法,所以它不是最大的问题