【问题标题】:PHP stream/file upload and max_input_varsPHP 流/文件上传和 max_input_vars
【发布时间】:2012-10-14 13:24:56
【问题描述】:

当我执行从 Java 到 PHP 的流上传时,我有时会收到一个 PHP 错误,提示输入变量超出了max_input_vars 的限制。

起初,我不明白为什么。我先解释一下:

正在使用类似于以下的方法上传文件:

// get file data from input stream
$putdata = fopen("php://input", "r");
$tmp = tmpfile();
filesize = stream_copy_to_stream ($putdata, $tmp);
fclose ($putdata);

// copy temp stream into destination stream
$target = fopen('myfile.dwg', "w");        
fseek($tmp, 0, SEEK_SET);
stream_copy_to_stream($tmp, $target);
fclose($target);
fclose ($tmp);

为了弄清为什么 PHP 会给我这样的警告,我对正在发送的数据进行了转储:

file_put_contents ('input_vars.log', print_r ($_REQUEST, true));
file_put_contents ('php_input.log', file_get_contents ('php://input'));

这是有趣的部分: 上传的文件为 1.8 兆字节。生成的日志是:

  • input_vars.log => 5 兆字节,90,000 行
  • php_input.log => 20 兆字节,283,000 行

现在错误消息突然看起来合法了。 php_input.log 只包含字节码,但 input_vars.log 的格式如下:

Array
(
    [filename] => 0018-101-001_67.dwg
    [versionId] => 11253
    [filetype] => dwg
    [‘á‹Úê-8øFj–sÙ/ghÔ÷JJÐWhvPV] => ...
    ....
)

前三个键是通过 GET 发送的,其余的都是文件数据。如果我搜索并计算 => 的匹配项,我会得到 25,954 个匹配项。然后我假设 REQUEST 拥有 26,000 个密钥。

现在,回到我的问题: 我已经多次提高了max_input_vars 的值,现在它的值是30000。我是否应该忽略此安全设置,并将其设置为尽可能高? 我担心的是 PHP 会从 REQUEST 数组中删除大于30000 的部分,从而导致文件损坏。

将此值设置得太高是否存在任何安全问题?是否有更好的方式将文件上传到 PHP?

【问题讨论】:

    标签: php upload stream


    【解决方案1】:

    您没有展示如何用 Java 上传文件。 PHP 将普通的 POST 请求视为来自表单并尝试从中解析字段 - 如果数据是二进制数据,这是个坏主意。您最好模拟 HTML 文件上传表单的功能并发送“多部分帖子”(example)。然后像从表单接收一样使用PHP's standard file upload handling functionality

    【讨论】:

    • 文件正在通过缓冲内容到HttpUrlConnection的输出流中被上传,并且一旦我们调用getInputStream()方法就被有效地上传。
    【解决方案2】:

    在“php.net”(PHP.NET - max_input_vars)本身中,他们报告说您增加此设置的值没有问题,但是,这是“使用此指令减少拒绝的可能性的一种形式”使用哈希冲突的服务攻击。”。

    要解决这个僵局,你可以尝试在运行时设置变量值,使用下面的函数:

    ini_set("max_input_vars", 30000);
    

    我最近遇到了这个问题,调用是远程的,没有给配置运行时,所以不幸的是我们不得不增加这个值。

    到目前为止,我们没有问题,但想法是未来,修改代码以按部分发送数据,从而使代码保持在语言的标准当前配置内。

    【讨论】:

    • 我们现在已经通过 htaccess 增加了设置(根据文档,ini_set() 在这种情况下是非法的)。但是,我们不能说我们设置的限制是否足够。
    • 这很复杂,您必须在 php.ini 中根据文件生成的索引数量在运行时从 JAVA 中配置此变量。但是因为你不能移动文件或路径并在PHP中进行处理?
    【解决方案3】:

    也许尝试将 enable_post_data_reading 指令设置为“false”(或“Off”)以防止 PHP 解析文件正文?

    顺便说一句,如果您使用的是 PHP 5.3.9,您应该修补 max_input_vars vulnerability

    【讨论】:

    • 我无法从手册中读取该值是否可以通过 ini_set() 或 htaccess 设置。你知道这个问题的答案吗?顺便说一句,我们使用的是 PHP 5.4.4。
    • 通过 .htaccess,据我所知。在php.net/manual/en/ini.list.php 声明“可更改 - PHP_INI_PERDIR”(可以在 php.ini、.htaccess、httpd.conf 或 .user.ini 中设置条目(自 PHP 5.3 起))。应设置为“关闭”以防止解析。无论如何,设置好后检查你的phpinfo()看看是否有效。
    • 这实际上似乎解决了问题!太感谢了! :)
    • 很高兴我能帮上忙,大卫!
    【解决方案4】:

    你想做的是PUT upload,你不应该把它当作一个POST来处理;或者 - 至少 - 将 Content-Type HTTP 标头设置为 application/octet-stream

    【讨论】:

      【解决方案5】:

      尝试增加 php.ini 中的 post_max_size 、 upload_max_filesize 和 max_file_uploads 的大小

      【讨论】:

        【解决方案6】:

        接受的答案是有效的,但您必须注意关闭enable_post_data_reading 也意味着关闭自动填充$_POST$_GET$_REQUEST 数组。由于我广泛使用这些数组,我决定将enable_post_data_reading 限制为专用于文件上传的特定文件:

        <Files "UploadFile.php">
            php_value enable_post_data_reading Off
        </Files>
        

        因为我是使用 ajax 请求上传的,所以我可以这样设置用户定义的标头:

        oXhr.setRequestHeader("X-File-Name", I_sFileName);
        oXhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        

        通过这样做,你绕过了使用$_REQUEST$_POST的需要,因为你在php中得到了信息:

        $fileName = $_SERVER['HTTP_X_FILE_NAME'];

        【讨论】:

          猜你喜欢
          • 2012-05-05
          • 1970-01-01
          • 2017-05-12
          • 2011-04-28
          • 1970-01-01
          • 2011-07-12
          • 2021-03-27
          • 2011-06-07
          • 2017-05-24
          相关资源
          最近更新 更多