通过Web方式访问的时候,Content-Type是文件类型最权威的描述。此时文件扩展名在大多数情况下都会被忽略。
HTTP 1.1规范
只有在完全没有Content-Type响应头时,接收方才可能需要根据网页的内容并结合资源定位URI后缀,去主动检测传输的媒体类型。
正文开始前的游戏
在web服务器新建文件demo.abcde,并写入如下代码,然后浏览器访问。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<h2>HelloWorld</h2>
</body>
</html>
此时你将会看到如下这种景观。
是的,后缀是.abcde的文件被当作了html文件解析。
当然,如果里面包含恶意的XSS Payload时,依旧可以。
如果你感觉并不惊讶,一切尽在掌握,那么便可以跳过这篇文章了,但是如果你有些恍然,那么你可以继续读下去,来获取答案。
内容嗅探机制(content sniffing)
- 浏览器会检查文件的静态特征,与已知的文件格式进行对比,推测文件类型;
- 不符合格式特征的文档,如HTML文件,会扫描文件的内容,查找特定的子字符串(如寻找熟悉的标签
<body>
<font>
等等); - 有些浏览器还会考虑URL里路径部分的文件后缀。
浏览器之间的解析差异
以HTML文件来说,在进入浏览器的内容检测机制后,
Chrome和Firefox会自动检测在文件起始位置有没有发现某几个预定义的HTML标签;
Firefox只要碰到URL里面有.html这样的文件扩展名,就会急切的把文档判断为HTML类型,即使没有发现可以识别的标签代码。
IE浏览器,默认的就会把文档归为HTML格式处理。
Opera浏览器,会扫描数据里面的前1000个字节,看是否有能识别的HTML标签。
如何迫使浏览器进入内容嗅探阶段
格式错误的MIME Type写法
从服务器端返回的MIME类型写法无效时,浏览器也可能会主动去猜测页面类型。
Content-Type响应头的值由斜线分隔
Content-Type: 类型/子类型[;charset=编码方式]
如 Image/png
。
- 当缺少中间写斜线时,几乎所有浏览器都会主动去猜测内容的类型。
- 在类型位置,如果包含空格和某些控制符时,也会启用内容检测。
特殊的Content-Type 值
application/octet-stream
很多web服务器对各种不透明的非Web文件,如供下载的可执行文件活归档文件,如果没有更合适的Content-Type与之相匹配,都会默认把他们归到application/octet-stream
类型。
经过测试当访问如下文件时,edge与ie浏览器仍然会进入内容嗅探机制,最终当作HTML解析。
而其他浏览器,Firefox、Chrome、Opera则会当作附件下载。
<?php
header('Content-Type: application/octet-stream');
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<h2>HelloWorld</h2>
</body>
</html>
text/plain
某些web服务器会把text/plain
作为某些类型响应的默认值。
在旧版本的IE浏览器(IE8及以下),在对这类文档也会通过内容检测来判断类型。
无法识别的Content Type类型
旧版本的IE浏览器(IE8及以下)在碰到浏览器内部原本可以识别的文件格式,但是出于为止的原因该文件不能被正常解析时,浏览器会进入内容嗅探机制。
例如文件是图片格式,但是内容却是HTML代码,此时浏览器无法正确解析时,会进入嗅探机制,因此会当作HTML文件进行解析。
直白一点,在web服务器上新建一个文件,demo.png,然后写入上面重复很多次的HTML代码。
然后用低版本的IE浏览器访问(IE8及以下),依旧会当作HTML解析。
[NOTE] 推荐一款工具IETester,一个ie浏览器多版本测试工具。
其他非HTTP内容
当通过file协议ftp等协议访问文件时,是不存在Content-Type
字段的。
对file协议的文件处理,完全依赖于文件扩展名的信息和内容嗅探。
当通过ftp协议访问文件时,大多数浏览器都不太关心文件扩展名,直接进入内容嗅探阶段。
唯一的例外是Opera,扩展名优先级永远高于内容嗅探。
完全陌生的文件扩展名
对于完全陌生的文件扩展名,则会有两种处理方式。
- 注册给外部程序处理的其他扩展名,IE浏览器通常会启动外部程序来打开这些文件,但大多数其他浏览器会启用内容检测。
- 如果是一个完全陌生的扩展名,那么所有的浏览器都会启用内容检测机制,情况和这些文件是通过HTTP形式加载且没有哦content-type设置时的处理一模一样。
也正是文章开始前的那个小游戏那样,几乎所有浏览器都会启用内容检测机制。
防御措施
Content-Type
通过Web方式访问的时候,Content-Type是文件类型最权威的描述。此时文件扩展名在大多数情况下都会被忽略。
X-Content-Type-Options
X-Content-Type-Options: nosniff
禁用浏览器的内容嗅探机制。
Content-Disposition
将数据体设定为附件
Content-Disposition: attachment[;filename="文件名"]
此时大多数浏览器在碰到这个响应头时,不会直接解析或显示其返回的数据,而是显示文件下载对话框。