【问题标题】:In PHP, how can I detect that input vars were truncated due to max_input_vars being exceeded?在 PHP 中,如何检测到输入变量因超出 max_input_vars 而被截断?
【发布时间】:2012-08-29 01:41:57
【问题描述】:

我知道E_WARNING是由PHP生成的

PHP Warning: Unknown: Input variables exceeded 1000

但是如何在我的脚本中检测到这一点?

【问题讨论】:

  • 有趣的问题,但您是否预计您将真的需要非常多的输入变量?你的问题背后的逻辑是什么?因为如果您实际上需要 ~1000 个输入变量,那么您很可能会错误地解决问题。
  • 您可以通过创建自己的错误处理程序来拦截警告。见stackoverflow.com/questions/1241728/can-i-try-catch-a-warning
  • @rdlowrey 这不是面向公众的网站;它解决了需要将大量状态从浏览器传输到服务器的特定业务目的。
  • @rdlowrey 我没有问如何减轻或重新设计以避免问题。我问如何检测它。
  • @Andy 这就是为什么我提出建议但没有回答的原因。

标签: php apache security


【解决方案1】:

count($_POST, COUNT_RECURSIVE) 不准确,因为它计算了数组树中的所有节点,而 input_vars 只是终端节点。例如,$_POST['a']['b'] = 'c' 有 1 个 input_var,但使用 COUNT_RECURSIVE 将返回 3。

php://input 不能与enctype="multipart/form-data" 一起使用。 http://php.net/manual/en/wrappers.php.php

由于这个问题只出现在 PHP >= 5.3.9 中,我们可以使用匿名函数。下面递归计算数组中的终端数。

function count_terminals($a) {
  return is_array($a)
           ? array_reduce($a, function($carry, $item) {return $carry + count_terminals($item);}, 0)
           : 1;
}

【讨论】:

    【解决方案2】:

    对我有用的是这个。首先,我把它放在我的脚本/处理程序/前端控制器的顶部。这是错误将被保存的地方(或者 $e0 将为空,这没关系)。

    $e0 = error_get_last();
    

    然后我运行一堆其他处理,引导我的应用程序,注册插件,建立会话,检查数据库状态 - 很多事情 - 无论超过这个条件我都可以完成.. 然后我检查这个 $e0 状态。如果它不为空,我们有一个错误,所以我退出(假设 App 是一个大类,里面有很多你的魔法)

    if (null != $e0) {
        ob_end_clean(); // Purge the outputted Warning
        App::bail($e0); // Spew the warning in a friendly way
    }
    

    为您自己的状态调整和调整错误处理程序。

    注册错误处理程序不会捕获这种情况,因为它在您的错误处理程序注册之前就已经存在。

    检查输入变量计数是否等于最大值是不可靠的。

    上面的 $e0 将是一个数组,type => 8,line => 0;该消息将明确提及 input_vars,因此您可以通过正则表达式匹配来创建一个非常狭窄的条件并确保特定情况的正确识别。

    另请注意,根据 PHP 规范,这是警告而非错误。

    【讨论】:

    • 谢谢,我添加了类似的东西,它似乎工作得很好。 (过滤器中的某处:$e0 = error_get_last(); if (null != $e0) { Log::error($e0); throw new PostSizeExceededException(); }
    【解决方案3】:

    “足够接近”的方法是检查if( count($_POST, COUNT_RECURSIVE) == ini_get("max_input_vars"))

    如果 POST 变量的数量恰好在限制范围内,这将导致误报,但考虑到默认限制为 1000,这不太可能成为问题。

    【讨论】:

    • $_GET$_COOKIE 都被视为输入变量。即使填充了 $_POST 数组,两者都可以包含值。这如何解释这些情况?
    • 如果我理解正确,max_input_vars 分别适用于$_GET$_POST$_COOKIE
    • 我不太熟悉max_input_vars 在这种情况下的确切行为——这就是我问的原因。这似乎是一个丑陋的 hack(不是您的代码,PHP 实现),因为超全局变量本身就是一个丑陋的 hack。
    • @Kolink 我在您的答案中添加了 COUNT_RECURSIVE。似乎它可以检测到问题。谢谢。
    • 这是很多工作才发现的。如果您想拥有更多控制权,可能需要扩展 PHP。
    【解决方案4】:
    function checkMaxInputVars()
    {
        $max_input_vars = ini_get('max_input_vars');
        # Value of the configuration option as a string, or an empty string for null values, or FALSE if the configuration option doesn't exist
        if($max_input_vars == FALSE)
            return FALSE;
    
        $php_input = substr_count(file_get_contents('php://input'), '&');
        $post = count($_POST, COUNT_RECURSIVE);
    
        echo $php_input, $post, $max_input_vars;
    
        return $php_input > $post;
    }
    
    echo checkMaxInputVars() ? 'POST has been truncated.': 'POST is not truncated.';
    

    【讨论】:

      【解决方案5】:

      尽快在您的脚本中调用error_get_last()(在您有机会导致错误之前,因为它们会掩盖这个错误。)在我的测试中,如果适用,max_input_vars 警告将会出现。

      这是我的测试脚本,max_input_vars 设置为 100:

      <?php
      if (($error = error_get_last()) !== null) {
          echo 'got error:';
          var_dump($error);
          return;
      }
      unset($error);
      
      if (isset($_POST['0'])) {
          echo 'Got ',count($_POST),' vars';
          return;
      }
      ?>
      <form method="post">
      <?php
      for ($i = 0; $i < 200; $i++) {
          echo '<input name="',$i,'" value="foo" type="hidden">';
      }
      ?>
      <input type="submit">
      </form>
      

      达到 var 限制时的输出:

      got error:
      array
        'type' => int 2
        'message' => string 'Unknown: Input variables exceeded 100. To increase the limit change max_input_vars in php.ini.' (length=94)
        'file' => string 'Unknown' (length=7)
        'line' => int 0
      

      在 Ubuntu 上使用 PHP 5.3.10 和 Apache 2.2.22 测试。

      我会犹豫是否明确检查此错误字符串、稳定性(他们可以更改它)和一般 PHP 良好实践。我更喜欢将所有 PHP 错误转换为异常,例如 this(单独的子类可能有点过头了,但我喜欢这个示例,因为它允许 @ 错误抑制。)来自 error_get_last() 会有点不同,但应该是很容易适应。

      我不知道是否还有其他可能被此方法捕获的执行前错误。

      【讨论】:

      • 好答案。但是,将错误转化为异常不适用于这种情况,对吗?因为在设置处理程序之前引发了错误。如果只有一个可以检查的实际错误代码,例如 PHP_ERROR_TOO_MANY_INPUT_VARS。
      • @Andy 您可以将错误保存在某处并在安装时手动将其泵入错误处理程序,或者在您准备好时将其作为异常抛出。也许匹配字符串以检测此特定错误以便能够更优雅地处理。查看字符串可能没有那么糟糕,因为我猜这是检测特定错误的唯一方法,所以大概 PHP 不会更改字符串。
      【解决方案6】:

      这样的事情呢:

      $num_vars = count( explode( '###', http_build_query($array, '', '###') ) );
      

      你可以对$_POST$_GET$_COOKIE随便重复一遍。

      仍然不能被认为是 100% 准确,但我想它已经非常接近了。

      【讨论】:

        猜你喜欢
        • 2023-03-24
        • 1970-01-01
        • 2020-08-16
        • 2015-04-15
        • 2012-09-22
        • 2015-05-12
        • 1970-01-01
        • 2013-01-13
        • 2015-08-02
        相关资源
        最近更新 更多