【问题标题】:What's wrong with using $_REQUEST[]?使用 $_REQUEST[] 有什么问题?
【发布时间】:2011-01-09 16:39:03
【问题描述】:

我在这里看到很多帖子说不要使用$_REQUEST 变量。我通常不会,但有时它很方便。它有什么问题?

【问题讨论】:

  • 从 php 5.3 开始,默认的 php.ini 表示只有 GET 和 POST 数据被放入$_REQUEST。请参阅php.net/request_order,当我期望 cookie 数据位于$_REQUEST 中时,我偶然发现了这个向后兼容中断,并想知道为什么它不起作用!因此,避免使用 $_REQUEST 的最大原因是现在您的脚本无法设置 request_order 本身(它是 PHP_INI_PERDIR),因此 php.ini 更改很容易打破您的脚本所基于的假设。最好将这些假设直接放入您的脚本中。

标签: php methods form-submit


【解决方案1】:

结合使用$_GET$_POST 的输入绝对没有错。事实上,这就是你几乎一直想做的事情:

  • 对于通常通过 GET 提交的普通幂等请求,您想要的数据量可能不适合 URL,因此实际上已将其更改为 POST 请求。

  • 对于一个有实际效果的请求,你必须检查它是通过POST方法提交的。但这样做的方法是明确检查$_SERVER['REQUEST_METHOD'],而不是依赖$_POST 为空来获取GET。无论如何,如果方法是POST,您仍然可能希望从 URL 中取出一些查询参数。

不,$_REQUEST 的问题与混淆 GET 和 POST 参数无关。它还默认包含$_COOKIE。而且 cookie 根本不像表单提交参数:您几乎永远不想将它们视为同一事物。

如果您不小心在您的网站上设置了一个与您的表单参数之一同名的 cookie,那么依赖该参数的表单将神秘地停止正常工作,因为 cookie 值覆盖了预期的参数。如果您在同一个站点上有多个应用程序,这很容易做到,并且当您只有几个使用旧 cookie 的用户时很难调试- 其他人可以复制。

您可以使用 PHP 5.3 中的 request_order 配置将此行为更改为更合理的 GP(没有 C)顺序。如果这是不可能的,我个人会避免使用$_REQUEST,如果我需要一个组合的 GET+POST 数组,请手动创建它。

【讨论】:

  • 这些情况(数据太长无法通过 GET 提交)是例外,而不是规则
  • 不错的答案。我还注意到 Zend Framework GET 和 POST 参数被组合成 1 个请求对象。直到我读了你的帖子,我才感到震惊。
  • 默认情况下,request_order 配置在 PHP 5.4+ 的 php.ini 中设置为“GP”,所以我会说去吧...但与往常一样,请谨慎行事。
  • 如果 $_POST 是 a="foo" 和 $_COOKIE a="bar",那么这里会有任何覆盖/冲突吗?
【解决方案2】:

我一直在浏览PHP Internals 上的一些新闻组帖子,并发现了有关该主题的有趣讨论。最初的主题是关于其他的,但 PHP 世界的安全专家(如果不是 the)Stefan Esser 的评论将讨论转向在一些帖子中使用 $_REQUEST 的安全影响。

引用Stefan Esser on PHP Internals

$_REQUEST 是 PHP 中最大的设计缺陷之一。每个应用程序使用 $_REQUEST 很可能容易受到延迟跨站请求的影响 伪造问题。 (这基本上意味着,例如,一个名为 (age) 的 cookie 存在它将始终覆盖 GET/POST 内容,因此 将执行不需要的请求)

later reply to the same thread

这与有人可以伪造 GET、POST 无关; COOKIE 变量。 这是关于 COOKIE 将覆盖 GET 和 POST 数据的事实 请求。

因此,我可以用一个 cookie 感染你的浏览器,例如 action=logout 并且从那天起您将无法使用该应用程序 因为 REQUEST[action] 将永远注销(直到你 手动删除cookie)。

用 COOKIE 感染你是如此简单......
a) 我可以在子域上的任何应用程序中使用 XSS 漏洞
b) 曾经尝试为 *.co.uk 或 *.co.kr 在您拥有 有单域吗?
c) 其他跨域的方式……

如果您认为这不是问题,那么我可以告诉您 有一个简单的可能性来设置 f.e.产生的 *.co.kr cookie 在几个 PHP 版本中只返回白页。 想象一下:只需一个 cookie 即可杀死 *.co.kr 中的所有 PHP 页面

并且通过在对 *.co.kr 有效的 cookie 中设置非法会话 ID 名为 +PHPSESSID=illegal 的变量,您仍然可以对每个 PHP 执行 DOS 在韩国使用 PHP 会话的应用程序...

讨论继续进行一些帖子,并且读起来很有趣。


如您所见,$_REQUEST 的主要问题不在于它有来自 $_GET 和 $_POST 的数据,还有来自 $_COOKIE 的数据。列表中的其他一些人建议更改填充 $_REQUEST 的顺序,例如首先用 $_COOKIE 填充它,但是 this could lead to numerous other potential problems, for instance with Session handling

您可以完全从 $_REQUEST 全局中省略 $_COOKIES,这样它就不会被任何其他数组覆盖(实际上,您可以将其限制为标准内容的任何组合,例如 PHP manual on the variable_order ini setting 告诉我们:

variable_order 设置 EGPCS(Environment、Get、Post、Cookie 和 Server)变量解析的顺序。例如,如果 variables_order 设置为“SP”,那么 PHP 将创建超全局变量 $_SERVER 和 $_POST,但不会创建 $_ENV、$_GET 和 $_COOKIE。设置为 "" 意味着不会设置超全局变量。

但话又说回来,您也可以考虑完全不使用 $_REQUEST,因为在 PHP 中您可以在自己的全局变量中访问 Environment、Get、Post、Cookie 和 Server,并且减少了一个攻击向量。您仍然需要清理这些数据,但不必担心这一点。


现在您可能想知道,为什么 $_REQUEST 到底存在,为什么它没有被删除。这也是在 PHP Internals 上提出的。在 PHP Internals 上引用 Rasmus Lerdorf 关于Why does $_REQUEST exist?

我们删除的此类内容越多,人们就越难 快速迁移到更新、更快和更安全的 PHP 版本。那 比一些“丑陋”的遗产更让每个人感到沮丧 特征。如果有正当的技术原因,性能或 安全性,那么我们需要认真研究一下。在这种情况下, 我们应该关注的不是我们是否应该删除 $_REQUEST 但我们是否应该从中删除 cookie 数据。多种配置 已经这样做了,包括我自己的,并且有很强的有效性 在 $_REQUEST 中不包括 cookie 的安全原因。大多数人使用 $_REQUEST 表示 GET 或 POST,没有意识到它也可能包含 cookie,因此坏人可能会进行一些 cookie 注入 技巧并打破幼稚的应用程序。

无论如何,希望能有所启发。

【讨论】:

  • 我认为讨论有点过于笼统了。实际的问题是开发人员不知道,而不是 $_REQUEST 本身存在 cookie。例如,固定的 PHPSESSID 将使用当前会话处理代码使用每个域的 cookie 进行重置。对于某些应用程序,可能确实需要覆盖请求变量的 cookie(例如,sort_order=ASC 覆盖搜索表单 GET 变量)。尽管明确地以这种行为进行编码更为明智。
  • 遗憾的是,Rasmus 在 2009 年对此发表了评论,但现在 $_REQUEST 在 2015 年基本相同。
【解决方案3】:

$_REQUEST 指的是各种请求(GET、POST 等)。这有时很有用,但通常最好指定确切的方法($_GET、$_POST 等)。

【讨论】:

  • 这个答案描述了 $_REQUEST 是什么,但它没有回答问题。
  • 他是说最好知道什么类型的请求将被传入并为该特定请求编码。
【解决方案4】:

$_REQUEST 通常被认为是有害的,原因与简单到中等复杂性的数据转换通常在应用程序代码中执行而不是在 SQL 中声明的原因相同:一些程序员很烂。

因此,如果有人倾向于在任何地方使用$_REQUEST,我可以通过 GET 做任何我可以通过 POST 做的事情,这意味着在我的(恶意)网站上设置<img> 标签,导致用户登录到您的电子邮箱。商务模块默默地购买产品,或者我可以让他们点击链接,这会导致危险行为或泄露敏感信息(可能对我来说)。

但是,这是因为新手,或者至少是没有经验的 PHP 程序员犯了简单的错误。首先,知道什么类型的数据什么时候合适。例如,我有一个 Web 服务,它可以返回 URLEncoding、XML 或 JSON 的响应。应用程序通过检查 HTTP_ACCEPT 标头来决定如何格式化响应,但可以通过发送 format 参数专门强制转换为一个。

在检查格式参数的内容时,它可以通过查询字符串或 postdata 发送,这取决于多种因素,其中最重要的是调用应用程序是否希望将 "&format=json" 与它的要求。在这种情况下,$_REQUEST 非常方便,因为它让我不必输入如下内容:

$format = isset($_POST['format']) ? $_POST['format'] 
    : (isset($_GET['format']) ? $_GET['format'] : null);

我不打算再啰嗦了,但我只想说$_REQUEST 的使用并没有被劝阻,因为它本质上是危险的——它只是另一种工具,可以完全按照它的要求去做,无论你是否理解这些含义与否 - 导致这个问题的是一个可怜的、懒惰的或没有经验的程序员的差劲、懒惰或不知情的决定。

如何安全使用$_REQUEST


  1. 了解您的数据:您应该对将获得什么样的数据有所期待,因此请相应地对其进行清理。数据库的数据? addslashes()*_escape_string()。要将其展示给用户吗? htmlentities()htmlspecialchars()。期待数字数据? is_numeric()ctype_digit()。事实上,filter_input() 及其相关函数的设计目的只是检查和清理数据。始终使用这些工具。
  2. 不要直接访问用户提供的超全局数据。养成每次都清理数据的习惯,并将数据移动到清理变量中,即使它只是 $post_clean。或者,您可以直接在超全局变量中进行清理,但我提倡使用单独变量的原因是因为这样做很容易发现代码中的漏洞,因为 anything 直接指向超全局变量而不是它的sanitized 等效项被视为危险错误。
  3. 知道您的数据应该来自哪里。 参考我上面的示例,允许通过 GET 或 POST 发送响应格式变量是完全合理的。我还允许通过任一方法发送“动作”变量。但是,操作本身对于可接受的 HTTP 动词有非常具体的要求。例如,对服务使用的数据进行更改的函数只能通过 POST 发送。对某些类型的非特权或低特权数据(例如动态生成的地图图像)的请求可能会响应来自任一方法的请求。

总之,记住这个简单的规则:

安全就是你创造的,人们!

编辑:

强烈推荐bobince的建议:如果可以的话,将php.ini中的request_order参数设置为“GP”;也就是说,没有 cookie 组件。在 98% 以上的情况下,这几乎没有合理的理由,因为 cookie 数据几乎不应该被认为与查询字符串或 postdata 相当。

P.S.,轶事!

我认识一位程序员,他认为$_REQUEST 是一个简单存储数据的地方,可以通过超全局方式访问。重要的用户名和密码,文件路径,你命名它并存储在$_REQUEST。当我告诉他该变量的行为方式时,他有点惊讶(尽管不幸的是,这并不滑稽)。不用说,这种做法已经被废除了。

【讨论】:

    【解决方案5】:

    GET 请求应该是幂等的,而 POST 请求通常不是。这意味着$_GET$_POST 中的数据通常应该以不同的方式使用。

    如果您的应用程序使用来自$_REQUEST 的数据,则它对 GET 和 POST 请求的行为相同,这违反了 GET 的幂等性。

    【讨论】:

    • 但这不取决于实现吗? “Indempotent”对我来说是一个新词,但如果我理解正确的话,很容易想象设置一个不是 Indempotent 的 GET 情况。例如,页面计数器通常会在您每次请求给定 url 时增加。
    • @sprugman - 同样,您可能会遇到在同一个请求中同时拥有 GET 数据 POST 数据的情况,在这种情况下,请求方法在上下文化时基本上没有意义与请求数据。
    • sprugman,显然任何 GET 请求都会修改 something,因为它是由 Web 服务器记录的。它在应用程序的域中仍然可以是幂等的,而元数据并不重要。
    • @sprugman - 这里的一般想法是您不应该有修改数据的 GET 请求。这不好的一个典型例子是网络蜘蛛爬取您的网站并跟踪无意中修改数据的链接。例如 SO 帖子上的“标志”链接。
    • 那只是个小例子。如果我通过 ajax 使用 GET 会怎么样,因为它更快(正如这篇关于 carsonified carsonified.com/blog/dev/the-definitive-guide-to-get-vs-post 的帖子中所建议的那样)。
    【解决方案6】:

    很模糊。您真的不知道数据是如何到达您的,因为它携带 post、get 和 cookie 数据。我不一定认为这总是一件坏事,除非您需要了解或限制交付方式。

    【讨论】:

      【解决方案7】:

      我其实很喜欢使用它。它为您提供了使用 GET 或 POST 的灵活性,这对于大多数时间都发布数据的搜索表单之类的事情非常有用,但有时您会想说链接到特定搜索,因此您可以使用 GET 参数代替.

      此外,如果您查看许多其他语言(例如 ASP.NET),它们根本不会区分 GET 和 POST 变量。

      预计到达时间

      我从未使用 REQUEST 来获取 COOKIE 值,但我认为 Kyle Butt 在这篇文章的 cmets 中提出了一个很好的观点。使用 REQUEST 获取 COOKIE 值不是一个好主意。我相信他是对的,如果你这样做的话,确实存在跨站点请求伪造的可能性。

      此外,将内容加载到 REQUEST 的顺序由 php.ini 中的配置参数(variables_order 和 request_order)控制。因此,如果您通过 POST 和 GET 传入相同的变量,则实际进入 REQUEST 的变量取决于这些 ini 设置。如果您依赖于特定订单并且这些设置的配置与您预期的不同,这可能会影响可移植性。

      【讨论】:

      • 这是一个可怕的错误。你如何保证通过 POST 执行非幂等操作?
      • @Kyle - 不将其用于非幂等操作。我当然不会将它用于所有事情,只是指出它很有用,就像我在示例中提到的搜索一样。
      • 这个神奇的想法,_POST 是安全的,_GET 是不安全的。如果我没有正确使用您的软件,那么发送 POST 请求与 GET 请求对我来说几乎没有区别(如果有的话)。安全性在于您对数据的处理方式,而不在于数据的来源。至于简单的 XSS/Request 攻击,它完全有可能仅将 _REQUEST 用于对 POST 或 GET 有效的值,而将 _POST 仅用于应该发布的东西。常识,而不是神奇的超全球用法。
      • @Kyle - 我仍然不明白你为什么不能像使用 curl() 或 ajax post 之类的东西一样轻松地通过 POST 和 COOKIE 传递相同的数据。无论您使用 REQUEST、GET、POST 还是 COOKIE,所有数据最终都来自客户端,并且总是可以伪造的。
      • @zombat:您形成的 curl 请求不会以易受攻击的用户身份登录。您创建并放置在您的网站上的链接将。 @Dereleased:这不是神奇的想法,GET 仍然存在大量漏洞。但是信任来自登录用户的 POST 比来自登录用户的 GET 更安全。
      【解决方案8】:

      了解何时使用 POST、何时使用 GET 以及何时使用 cookie 很重要。使用 $_REQUEST,您正在查看的值可能来自其中任何一个。如果您希望从 POST、GET 或 COOKIE 中获取值,那么对于阅读您的代码的人使用特定变量而不是 $_REQUEST 会提供更多信息。

      其他人还指出,您不希望所有 POST 或 cookie 都被 GET 覆盖,因为它们都有不同的跨站点规则,例如,如果您在使用 $_REQUEST 时返回 ajax 数据,您容易受到跨站脚本攻击。

      【讨论】:

        【解决方案9】:

        唯一一次使用$_REQUEST 是一个不错的主意,那就是使用 GET。

        • 如果您使用它来加载 POST 值,则存在跨站点请求伪造的风险
        • 如果您使用它来加载 cookie 值,您将再次面临跨站点请求伪造的风险

        即使使用 GET,$_GET 的键入也比 $_REQUEST 短;)

        【讨论】:

        • 虽然我同意你的观点,但我认为重要的是要注意为什么这是正确的和/或危险的:当数据是 POSTDATA 很重要时,应该使用$_POST。当数据与所使用的 HTTP 动词无关时,应该使用$_REQUEST。在所有这些情况下,都应该仔细清理所有输入数据。
        • 我看不出数据来源与跨站点请求伪造的可能性有何关联。攻击者可以通过向用户提供指向您站点的 POST 表单来非常轻松地设置 POST 参数;无论提交来自 GET 还是 POST,您都将需要相同的反 XSRF 保护措施。
        • 滥用 GET 进行伪造要容易得多。例如,您可以简单地将 img 标签及其 src 设置为您想要的参数。它会在用户不知道它在那里的情况下工作。
        【解决方案10】:

        我可能仅在您想检索当前 url 或主机名时使用,但对于实际解析来自该 URL 的数据,例如使用 & 符号的参数,这可能不是一个好主意。一般来说,您不想对您正在尝试做的事情进行模糊的描述。如果您需要具体说明,那就是 $_REQUEST 不好的地方,如果您不需要具体说明,请随意使用它。我想。

        【讨论】:

        • 你想说什么?您是否将$_REQUEST$_SERVER['QUERY_STRING'] 混淆了?
        【解决方案11】:

        如果您知道自己想要什么数据,则应明确要求。 IMO、GET 和 POST 是两种不同的动物,我想不出为什么您需要混合发布数据和查询字符串的充分理由。如果有人有的话,我会很感兴趣。

        当您的脚本可能以相同的方式响应 GET 或 POST 时,使用 $_REQUEST 会很方便。尽管我认为这应该是一种极其罕见的情况,并且在大多数情况下,两个独立的函数来处理两个独立的概念,或者至少检查方法并选择正确的变量是首选。当不需要交叉引用变量可能来自哪里时,程序流通常更容易遵循。善待必须在 6 个月内维护您的代码的人。可能是你。

        除了由 REQUEST 变量中的 cookie 和环境变量引起的安全问题和 WTF(不要让我开始使用 GLOBAL),请考虑如果 PHP 开始原生支持其他方法(例如 PUT 和删除。虽然它们极不可能被合并到 REQUEST 超全局中,但它们可能会作为 on 选项包含在 variable_order 设置中。所以你真的不知道 REQUEST 是什么,什么是优先的,特别是如果你的代码部署在第三方服务器上。

        POST 比 GET 更安全吗?并不真地。最好在可行的情况下使用 GET,因为在您的日志中更容易看到您的应用程序在受到攻击时是如何被利用的。 POST 更适合影响域状态的操作,因为蜘蛛通常不会跟随它们,并且预测获取机制不会在您登录 CMS 时删除您的所有内容。但是,问题不在于 GET 与 POST 的优点,而在于接收者应该如何处理传入的数据以及为什么合并它不好,所以这实际上只是一个 BTW。

        【讨论】:

          【解决方案12】:

          我认为$_REQUEST 没有问题,但是我们在使用它时必须小心,因为它是来自 3 个来源 (GPC) 的变量的集合。

          我猜$_REQUEST 仍然可以使旧程序与新的php 版本兼容,但是如果我们开始新项目(包括新库),我认为我们不应该再使用$_REQUEST 来使程序更清晰。我们甚至应该考虑删除$_REQUEST 的使用并用包装函数替换它以使程序更轻量级,尤其是在处理大量提交的文本数据时,因为$_REQUEST 包含$_POST 的副本。

          // delete $_REQUEST when program execute, the program would be lighter 
          // when large text submitted
          unset($_REQUEST);
          
          // wrapper function to get request var
          function GetRequest($key, $default = null, $source = '') 
          {
            if ($source == 'get') {
              if (isset($_GET[$key])) { 
                return $_GET[$key]; 
              } else { 
                return $default; 
              }
            } else if ($source == 'post') {
              if (isset($_POST[$key])) { 
                return $_POST[$key]; 
              } else { 
                return $default; 
              }
            } else if ($source == 'cookie') {
              if (isset($_COOKIE[$key])) { 
                return $_COOKIE[$key]; 
              } else { 
                return $default; 
              }
            } else {
              // no source specified, then find in GPC
              if (isset($_GET[$key])) {
                return $_GET[$key];     
              } else if (isset($_POST[$key])) {
                return $_POST[$key]; 
              } else if (isset($_COOKIE[$key])) {
                return $_COOKIE[$key]; 
              } else {
                return $default; 
              } 
            }
          }
          

          【讨论】:

            【解决方案13】:

            Darren Cook: “从 php 5.3 开始,默认的 php.ini 表示只有 GET 和 POST 数据被放入 $_REQUEST。见 php.net/request_order 我只是 在期待 cookie 时偶然发现了这种向后兼容中断 数据在 $_REQUEST 中,想知道为什么它不起作用!”

            哇...由于升级到 PHP 5.3,我的一些脚本刚刚停止工作。做了同样的事情:假设在使用 $_REQUEST 变量时会设置 cookie。 随着升级完全停止工作。

            我现在使用 $_COOKIE["Cookie_name"]... 分别调用 cookie 值...

            【讨论】:

              【解决方案14】:

              只要确保在你的php.ini中设置正确的参数(下面是默认的,只要不设置为GPC,此处不使用 Cookie)

              request_order = "GP"
              

              这意味着 POST 会覆盖 GET 并且您会没事的。

              $_REQUEST 的原因仅仅是 $_GET 和 $_POST 的合并。 当通过页面上的大量链接发送表单和导航时,拥有一个地方来保存状态非常有用:$_REQUEST

              【讨论】:

                【解决方案15】:

                正如其他人所说,核心问题是它包含 cookie。

                在 PHP 7 中你可以这样做:

                $request = array_merge($_GET ?? [], $_POST ?? []);
                

                这避免了 cookie 问题,最坏的情况是给你一个空数组,最好的情况是 $_GET 和 $_POST 的合并,后者优先。如果你不太介意通过查询字符串允许 URL 注入参数,这很方便。

                【讨论】:

                  【解决方案16】:

                  这是非常不安全的。这也很尴尬,因为您不知道您收到的是 POST 还是 GET 或其他请求。在设计应用程序时,您真的应该知道它们之间的区别。 GET 非常不安全,因为它是在 URL 中传递的,除了页面导航之外几乎不适合任何东西。 POST 虽然本身也不安全,但提供了一个安全级别。

                  【讨论】:

                  • $_POST 和 $_GET 之间的安全性没有区别,只是您不能在浏览器 URL 栏中键入 POST 请求。然而,使用 POST 启动命令行 cURL 请求只需 5 秒。
                  • @Zombat:我们需要发起某种活动来说服人们 POST 本身并不安全,甚至比 GET 更安全。安全性在于您如何处理数据,而不是使用 HTTP 动词来获取数据。
                  • @Dereleased - 我提出了一张标志性的双色调云图片,上面有一些闪电来代表互联网,下面有“改变”这个词。
                  • @GuyT:这是一个非常狭隘的观点。这不仅仅是关于它有多“安全”,而是它有多机密。 GET 参数可以在浏览器 url 甚至浏览器历史记录中显示为自动完成。此外,安全性并不仅限于浏览器。例如,许多服务器会记录 http url,因此 url 中的任何内容都会被记录下来。在日志中显示用户名/密码确实会有所作为。为了安全起见,请始终避免将敏感信息作为 GET 参数传递。
                  • 特别是 Apache 访问日志。任何请求 url 都会被记录,任何有权访问日志的人都可以看到您的凭据通过。
                  猜你喜欢
                  • 2012-01-11
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-03-08
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-10-13
                  • 2011-04-24
                  相关资源
                  最近更新 更多