直接修改超全局变量


20

我见过人们(通常会写出好的代码)直接$_POST使用如下代码更改数组:

// Add some value that wasn't actually posted
$_POST['last_activity'] = time();

// Alter an existing post value
$_POST['name'] = trim($_POST['name']);

// Our pretend function
// Pass the entire $_POST array as data to work with in the function
// The function update_record() will read only the values we actually need
update_record($_POST);

// ...That sure was easier than creating a new array 
//  with only the $_POST values we actually need.

有意义的是,update_record()不应直接访问$ _POST,因此我们可以例如将其他数据数组传递给它,但是可以肯定这是惰性的,不良的设计,或者可能只是错误的?但是,我们仍然将有效数组传递给update_record(),那么为什么要创建一个新数组呢?

这不是问题的重点,只是用法的一个示例。但是,我听到很多人说不应使用$_REQUEST数据来完成此操作,这是不好的做法。但为什么?看起来足够无害。

例子:

  • 设置一个$_GET实际上不存在的默认(或发布)值

  • 添加$_POST表单提交后实际上未发布的值

  • 很早就在脚本中直接清理或过滤$_GET数组值或键(后备清理...为什么不呢?)

  • $_POST在表单提交之前手动设置一个值,以使用默认值填充输入(当输入读取$_POST它的默认值时;我已经这样做了)

  • 建立自己的$_SERVER价值观?当然,为什么不呢?

  • 其他人,例如$_COOKIE$_SESSION怎么样?当然我们必须直接修改那些对不对?那为什么不其他呢?

应该永远不要直接修改超全局变量 ,还是在某些情况下可以这样做?


我同意#1,#2和#3,因为这是意外的用法(尤其是#1和#2)。
凯文·佩诺

好问题。修改全局数组是错误的,就像使用全局值是错误的一样。同样,这些数组也有其用途(从外部传递参数),这使得更改它们成为弄乱代码内部的直接方法。但是,我相信其中一些数组可能在脚本开始时就被清除了,只是为了不引起代码内的问题。
塔德克

1
我使用的是OO输入数组包装器(隐式过滤),当$ _GET或$ _POST变量被篡改时,它们会显示一条附加通知。仍然可能,但应限制在狭窄的情况下。(跨模块信令,尽管只有调度员/前端控制器才需要它。)
mario

@mario:如果您可以看一下这个问题,我想听听更多关于您如何实现的信息:stackoverflow.com/questions/12954656
Wesley Murch 2012年

Answers:


16

鉴于PHP已经设置了这些超全局变量,所以我认为修改它们不是邪恶的。在某些情况下,这可能是解决问题的最佳方法……尤其是在处理无法轻松修改的第三方代码时。(他们可能会$_GET直接使用,或者假设某些密钥存在于$_SERVER等)

但是,总的来说,我认为编写自己的代码是一种不好的做法。$_REQUEST使用某些在幕后自动在每个页面上运行的过滤器修改数据可能会带来副作用。(请参阅“魔术引号”引起的所有问题以供证明。)

因此,如果您不打算这样做(自动过滤超全局变量),那么以下内容不会给您带来任何好处:

$_POST['foo'] = filter($_POST['foo']);

当您可以轻松做到时:

$foo = filter($_POST['foo']);

我认为这是更清晰,使整个网站的区别是$_POST$_GET始终未过滤的,不可信的数据,他们应该永远被原样使用。

通过将过滤后的值复制到另一个变量,您可以声称:“我了解我在做什么...我已经过滤了此输入,并且可以安全使用。”


感谢您的输入,我的互联网已经停了将近2天,所以我没有机会回复任何人。在示例中,我修改了$ _POST并将其用作传递给更新函数的数据数组,假设我们将在该函数中读取其他几个$ _POST键。我希望创建一个新的数组,但是我看到人们这样做了,所以我仍然不确定是否将其称为“错误代码”,但我认为至少是这样。我认为只要您有需要这样做,总会有更好的方法。
Wesley Murch

1
@Wesley,它“不好”的主要原因是它使您更有可能忘记对某些用户数据进行清理。在您的示例中,您修改了一个键,然后传递了整个数组。如果某些数据包含未经处理的恶意输入该怎么办?最好手动构建该新阵列,仅将所需的内容复制$_POST到其中,并在进行过程中进行消毒。关于其他人这样做……好吧,许多人编写了非常糟糕的PHP代码,但这也不是您的借口。:)
konforce 2011年

我认为除了清理数据外,我还应该强调超全球性滥用的其他应用。我可能应该将其全部排除在外,并写出一个更明确的问题,人们特别喜欢了解安全方面的内容,而常常忽略其余部分。别忘了,我真的很感谢您的反馈。我要等待一天,并给它打上复选标记,因为您已经解决了一些问题,但是在3分钟内有新问题时错过了投票方:)再次感谢!
Wesley Murch

9

我通常建议您不要修改预定义的超级全局变量,这样就可以清楚地看到什么是经过清理的数据以及什么是原始/不受信任的数据。

其他人可能建议,如果您在请求周期开始时清理超全局变量,则无需在其他地方担心它们。

当您需要它们时,我会始终将它们匹配:

$id = (int)$_POST['id'];

或类似。

在其他变量而言这是很好的做法,不写任何的$_GET$_POST$_REQUEST$_SERVER$_COOKIE$_SESSION但是有所不同,因为您通常希望将数据写入会话,然后将其持久化到会话中的不同请求之间。


2
这些全局变量应该消失的更多原因是可以在需要时调用以获取它们的对象/方法。为什么setcookie存在,但我们通过获得Cookie $_COOKIE?另外,由于$_COOKIE仅在当前会话开始时才设置,并且永远不会更新,因此它要求您在两个区域中都更改/设置cookie,以便以后的代码区域具有最新信息。
凯文·佩诺

谢谢詹姆斯,我已经离线了一段时间,所以我无法回应。长话短说-我同意你的看法。总有比写post / get / etc更好的解决方案,但是我仍然不确定它是否被认为是一个严格的坏主意,例如“ 永远都不要做 ”。因此,如果我再次遇到这种类型的代码,您是否认为我有权对草率的代码“大声疾呼”,还是有时可以巧妙,安全地使用它?
韦斯利·默奇

@Wesley如果说“永远做不到”,那么超级全局变量可能严格地是只读的-事实并非如此。出于上述原因,我称这是在您的应用程序代码中设置或覆盖它们的不好的做法。
米歇尔·费尔德海姆

3

你应该避免它。也许有一段时间您忘记对某些东西进行消毒,那么您可以检索危险数据。如果您在清理数据时将数据复制到新结构中

  • 你只得到,你想要什么/需要,而不是他在$_POST
  • 如果新创建的数组缺少某些键或完全丢失,则可能会出现错误

其他脚本可能会认为该数组未受影响并且可能会产生好奇。


2

我从来不喜欢修改超全局性的想法,因为它会引起误解。这是一种快捷的方法,可以做某事,而这几乎是一种更好的方法。

$_POST例如,如果更改的值,则表示软件接收到的数据不是。

真正的问题

在现实生活中,这成为一个大问题:

假设您正在团队中工作。在理想世界中,每个人都使用相同的语法,但是我们并不生活在理想世界中。一个开发人员John喜欢使用来访问发布的数据$_POST。他在后var中更改了一些内容:

$_POST['ranking'] = 2; // John has changed ranking from 1 to 2 for whatever reason

然后,您还有另一个开发人员Chris,他更喜欢使用它filter_input来访问输入的数据(即GET,POST,SERVER,COOKIE),以便在处理用户可以篡改的数据时保护软件。在软件方面,他需要获得的职位价值ranking。他的代码部分是John 之后的代码。

$ranking = filter_input(INPUT_POST, 'ranking', FILTER_SANITIZE_NUMBER_INT);
// $ranking = 1

在上面的示例中,通过更改超全局变量,您破坏了PHP。约翰$_POST['ranking']出于任何原因都将值设置为2,但是现在克里斯已经收到了值1

当我没有其他方法可以执行此操作时:

我参与了一个项目,该项目使用wordpress作为AWS负载平衡器背后的博客。这会更改的值$_SERVER['remote_address']。在这种情况下,其他开发人员别无选择,只能执行以下操作:

if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $parts = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
    $_SERVER['REMOTE_ADDR'] = $parts[0];
}

结论

几乎肯定有比改变超全球主义者更好的方法


1

我认为这里的真正问题是“为什么要修改主题?”。我看不出有任何正当理由。如果您需要清理输入,可能需要使用局部变量…

除非您的代码足够短(例如,少于50行),否则修改这些超级全局代码只会使您的代码难以维护和理解。

顺便说一下,您不需要将$ _POST传递给函数,因为它是一个超全局数组,即使在函数的本地范围内也可以访问。


3
但是他应该通过。否则,它很难测试,并且无法在没有任何(甚至更丑陋)hack的情况下用其他值调用函数/方法
KingCrunch 2011年

好吧,这取决于他的方法。如果仅设计用来解析$ _POST数组中的任何内容,则他不需要传递它。当然,如果它用于更一般/抽象的目的,那是对的。

2
@托马斯,我在这里同意金。即使它们是全局的,也不应在其他范围内使用全局的任何东西,因为它会引起紧密的耦合(这就是无法重用该功能的原因)。以您的示例为例,如果该功能是清除数据,为什么它只清除$_POST数据?传入$_POST使函数清除所有数据。
凯文·佩诺

0

在最初通过说没有理由修改超全局变量回答了这个问题之后,我将以一个我决定的时候为例来编辑这个答案。

我目前正在处理URL重写数据库表,其中该request列将用户定向到其相应的target列。

例如,一个request可能是,blog/title-here而其target可能是blog.php?id=1

因为blog.php期望$_GET变量,并且我不想更改header("Location:"),所以我要做的事情是这样的:

$uri    = explode('?', $uri_request)[0];
$params = explode('?', $uri_request)[1];
parse_str($params, $_GET);

这将创建一个$_GET包含该target列传递的预期参数的数组。

最终,除非您绝对必须这样做,否则我强烈建议您不要修改超全局变量。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.