【问题标题】:How to validate a file type against its extension?如何根据扩展名验证文件类型?
【发布时间】:2016-02-15 04:12:56
【问题描述】:

我想创建一个上传脚本,我想定义一些允许上传的文件类型,以防止通过重命名文件扩展名来欺骗我使用这个数组:PHP / Mime Types - List of mime types publically available? 首先它检查是否允许文件扩展名(例如 .xsl),然后它使用 finfo 获取 mimtype 以检查该数组以查看 mimetype 是否与扩展名匹配。

我上传了一个 .xsl 文件,finfo 将文件类型返回为 application/octet-stream,但该 xsl 扩展数组返回 application/x-msexcel,因此它不会相等且不会被验证。

我真的应该忘记与脚本的文件扩展名验证器匹配的 mimetype 并且我应该只检查文件扩展名吗?或者我该怎么办?

【问题讨论】:

  • 你是如何保证文件类型是“application/octet-stream”的,如果你在文件数组中这样做,它是不可靠的,它很容易被伪造。如果没有识别到​​文件类型标题“application/octet-stream”,则发送。这并不并非总是意味着上传的文件属于这种类型。您应该检查上传的文件,而不是 mime 类型的 $_FILES 数组
  • 八位字节流从 finfo::buffer 返回。然后我从数组中得到了 xsl 扩展的 mimetype,它是 x-msexell,所以它们不相等。我应该如何针对扩展验证文件 mimetype 以避免重命名技巧?只允许提供的扩展名列表,以便有人可以重命名并上传它。如何验证?
  • 好吧,我想你给了我线索。我正在使用 finfo::buffer 所以不可靠,我将其更改为带有物理文件的 file_open ,现在它返回 application/vnd.ms-excel 而不是 octet-stream 如预期的那样。但现在还有一个问题:当我将文件名重命名为 .txt 以欺骗上传时,如何验证这是否是允许的文件?我应该使用 array_exists 来传递这种类型以将数组键作为 .xsl 来查看这是不允许的吗?
  • 我认为,这就是验证的重点。如果 mime 类型是 application/vnd.ms-excel 它必须是具有 excel 扩展名的文件,否则您的 mime 类型检查将只有“一半”安全:)
  • 我通过加入以下所有列表 + .php 扩展名创建了这个数组 pastebin.com/d4ZQBe5A 或 gist.github.com/nimasdj/801b0b1a50112ea6a997,所有这些列表都缺少。请为此列表做出贡献,以使其尽可能准确和完整。 gist.github.com/plasticbrain/3887245pastie.org/5668002pastebin.com/iuTy6K6d 总计:截至 2015 年 11 月 16 日,共有 1223 次延期

标签: php mime-types mime file-type


【解决方案1】:

基本上你做对了。您永远不应该依赖从上传表单发送的 mime 类型标头,因为您可以轻松伪造它或者它不存在,那么您通常会得到 application/octet-stream 标头。

因此,这是检查文件扩展名是否与此文件扩展名允许的 mime 类型匹配的好方法。

我看到你链接了这个列表here。这肯定是一个很好的列表,但对于 php 来说并不真正有用,因为数组中的 ovverriden 太多了,例如:

$mimeTypes = array(
    'xlm' => 'application/vnd.ms-excel',//overridden
    'xlm' => 'application/x-excel',
    'xls' => 'application/excel',//overridden
    'xls' => 'application/vnd.ms-excel'
);

var_dump( $mimeTypes );

这只会输出两个值而不是四个,你应该使用这样的数组:

$mimeTypes = array(
    'xlm' => array( 'application/vnd.ms-excel', 'application/x-excel' ),
    'xls' => array( 'application/excel', 'application/vnd.ms-excel' ),
    'txt' => array( 'text/plain' )
);

var_dump( $mimeTypes );

因此,如果您已经拥有文件扩展名,则只需使用 in_array() 检查 mimetype。

这是一个您可以解决的基本示例。 注意:这不是一个有效的例子,但我想你知道我想指出的地方:

// you have your file, you said it´s excel but you uploaded it with extension txt
$filepath = "excel.txt";

if( strpos( $filepath, '.' ) === false ) {
    // no file extension
    // return ?
}
// get the file extension
// well there is surely a better way
$filenameParts = explode( ".", $filepath );
$fileExt = array_pop( $filenameParts );// return the las element of the array and REMOVES it from the array

// your fopen stuff to get the mime type
// ok let´s say we are getting back the follwing mime type
$fileMimeType = 'application/vnd.ms-excel';

// now check if filextension is in array and if mimetype for this extension is correct
if( isset( $mimeTypes[$fileExt] ) && in_array( $fileMimeType, $mimeTypes[$fileExt] ) ) {
    // file extension is OK AND mime type matches the file extension
} else {
    // not passed unknown file type, unknown mime type, or someone tricked you
    // your excel.txt should end up here
}

【讨论】:

  • 没有扩展名的文件怎么办?你如何禁止/允许它们被上传?
  • 对于文件大小,在 $_FILES 上使用 mb_strlen 是否可靠?还是应该只使用物理文件的文件大小?
  • 某些扩展名,如 .docs .docx .xlsm..... 未在此处列出。可以帮忙添加吗?
  • 我通过加入以下所有列表 + .php 扩展名创建了这个数组pastebin.com/d4ZQBe5A,所有这些列表中都缺少。请为此列表做出贡献,以使其尽可能准确和完整。 gist.github.com/plasticbrain/3887245pastie.org/5668002pastebin.com/iuTy6K6d 总计:截至 2015 年 11 月 16 日,共有 1223 次延期
  • 我已经更新了答案并添加了一个条件:strpos( $filepath, '.' ) === false,在这里你可以返回 false 或其他,如果没有文件扩展名,这也将避免它,如果文件本身被命名为“ xls",对于文件大小我不能给你一个好的建议,因为我不知道在这种情况下 file_size 的安全性,我总是使用 $_FILES 数组中的文件大小,没有问题
猜你喜欢
  • 2011-04-07
  • 1970-01-01
  • 2014-08-15
  • 1970-01-01
  • 1970-01-01
  • 2010-09-26
  • 2013-05-24
  • 1970-01-01
  • 2018-11-26
相关资源
最近更新 更多