HTTP协议安全头部X-Content-Type-Options引入的问题
前段时间测试MM反馈了一个问题,在富文本编辑器里上传的图片无法正常呈现。因为Jackie在本机的环境上没有观察类似的现象,而恰好那天测试环境的某个重要配项被改错了,于是Jackie想当然的归类为配置项错误引入的问题。但修改完测试环境的配置项后,测试反馈富文本编辑器内图片无法呈现的现象依然存在。
这下就有点麻烦了,问题是在版本上线的前一天晚上发现的,如果问题不能尽快处理,势必对第二天的版本上线产生影响。虽然逢上线必加班至深夜,但也不能让问题挂在Jackie手里。
时间紧急,问题诡异,Jackie马上放下手头的工作,全力投入分析工作中。
排查过程
- 如前所述,配置项修改正确后,问题现象仍然存在,因此首先排除了配置项配置引入问题的可能。
- 请测试MM帮忙复现问题,仔细观察之后,发现使用不同浏览器访问问题页面时可以观察到不同的现象:
- 使用Chrome访问页面时,富文本编辑器内的图片可以正常呈现。
- 使用IE9、IE11访问页面时,富文本编辑器内的图片无法呈现。从浏览器的呈现效果看,貌似是图片加载失败;但在调试器的网络面板查看页面资源的加载情况,可以观察到浏览器发起了图片的获取请求,而Web应用确实返回了图片信息;但就是没有呈现出来。
- 直接把富文本编辑器的内图片的URL复制出来,贴到浏览器的地址栏里访问,图片可以正常呈现。
 
- 在生产环境和体验环境上做对比验证,使用IE9、IE11访问这两个环境,查看富文本编辑器内的图片,没有观察到前述的问题,因此可以判定测试MM在测试环境上发现的问题是最近引入的,时间范围不超过2个星期,因为那段时间项目组早已切换为两周一迭代的节奏。
- 检查富文本编辑器相关代码的提交记录,发现最近的提交记录远在几个月前,因此基本可以排除富文本编辑器自身代码的问题。
- 肿么搞呢?似乎进入了死胡同。看来常规的方法都不见效,只能逐一排查代码提交记录了。
幸运的是提交记录不多,10分钟之内被Jackie扫描了一遍;大部分提交记录都是清白了,唯一可疑的提交记录来自于Jackie本人,果然这个问题只能由Jackie来定位和分析。为了解决AppScan报告中提到的“HTTP响应缺少安全头部”的警告,Jackie在发现问题的那天早上修改了Tomcat的配置,增加了安全头部相关的过滤器,而晚上留在办公室加班,目的就是要确认前述的警告是否已消除。
为了确认前述问题和HTTP安全头部的相关性,Jackie手工修改测试环境上Tomcat的配置,去掉了增加安全头部的过滤器,重启Tomcat后尝试重现问题,惊喜的发现,富文本编辑器内的图片恢复正常呈现,这说明HTTP响应增加安全头部之后,对基本功能产生了影响。
当前启用了HTTP协议的安全头部的如下几个:
- Strict-Transport-Security
- X-Frame-Options
- X-Content-Type-Options
- X-XSS-Protection
范围比较小,逐个排查之后,发现前述问题现象和X-Content-Type-Options相关,因此决定仍然启用HTTP安全头部的输出,但禁用X-Content-Type-Options,富文本编辑器内的图片可以正常呈现,同时不会对安全性造成很大的影响。
本来觉得修改Tomcat的配置和业务不相关,不会有什么问题,也没有过基本功能,结果偏偏天不遂人愿,还真让测试MM发现个诡异问题。看来侥幸心理不能有,该做的工作不能省,否则就得加班、加倍的补回来。
下面使用最少的样例代码来说明问题是如何发生的。
问题是如何发生的
准备复现环境
jsp页面
<%@ page session="false" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title><%=request.getServletContext().getServerInfo() %></title>
    </head>
        <div>
            <img alt="FileDownload" src="<%=request.getContextPath()%>/GetPicture">
        </div>
    <body>
    </body>
</html>
GetPicture的实现
如下这段代码的实现存在问题,使用流方式返回数据时,没有显式指定Content-Type。
package com.struts2.servlets;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
public class GetPictureServlet extends HttpServlet {
    private static final long serialVersionUID = -5935833295545479697L;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        String path = req.getServletContext().getRealPath("");
        byte[] buffer = new byte[4096];
        int count = 0;
        InputStream is = null;
        OutputStream os = null;
        try
        {
            is = new FileInputStream(new File(path, "tomcat.png"));
            os = resp.getOutputStream();
            while ((count = is.read(buffer)) > 0) {
                os.write(buffer, 0, count);
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(os);
        }
    }
}
web.xml
<servlet>
    <servlet-name>GetPictureServlet</servlet-name>
    <servlet-class>com.struts2.servlets.GetPictureServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>GetPictureServlet</servlet-name>
    <url-pattern>/GetPicture</url-pattern>
</servlet-mapping>
使用浏览器观察
重温一些安全相关的HTTP响应头中对X-Content-Type-Options的介绍。
互联网上的资源有各种类型,通常浏览器会根据响应头的Content-Type字段来分辨它们的类型。例如:”text/html”代表html文档,”image/png”是PNG图片,”text/css”是CSS样式文档。然而,有些资源的Content-Type是错的或者未定义。这时,某些浏览器会启用MIME-sniffing来猜测该资源的类型,解析内容并执行。
使用浏览器调试面板来观察HTTP响应的头部,对上文做验证。
- 未启用HttpHeaderSecurityFilter时,HTTP响应的头部如下Content-Length:5103
 Date:Mon, 02 May 2016 05:07:22 GMT
 Server:Apache-Coyote/1.1
 
- 修改$CATALINA_BASE/conf/web.xml,启用HttpHeaderSecurityFilter<filter>
 <filter-name>httpHeaderSecurity</filter-name>
 <filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
 <async-supported>true</async-supported>
 </filter>
 使用浏览器的调试面板观察Tomcat响应浏览器请求时的HTTP头部 Cache-Control:private
 Content-Length:5103
 Date:Mon, 02 May 2016 05:42:00 GMT
 Expires:Thu, 01 Jan 1970 08:00:00 CST
 Server:Apache-Coyote/1.1
 Strict-Transport-Security:max-age=30;includeSubDomains
 X-Content-Type-Options:nosniff
 X-Frame-Options:SAMEORIGIN
 X-XSS-Protection:1; mode=block
 此时,使用浏览器访问页面时,图片不能正常呈现。 
- 修改$CATALINA_BASE/conf/web.xml,禁用X-Content-Type-Options特性<filter>
 <filter-name>httpHeaderSecurity</filter-name>
 <filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
 <async-supported>true</async-supported>
 <init-param>
 <param-name>blockContentTypeSniffingEnabled</param-name>
 <param-value>false</param-value>
 </init-param>
 </filter>
 使用浏览器的调试面板观察Tomcat响应浏览器请求时的HTTP头部 Cache-Control:private
 Content-Length:5103
 Date:Mon, 02 May 2016 05:50:39 GMT
 Expires:Thu, 01 Jan 1970 08:00:00 CST
 Server:Apache-Coyote/1.1
 Strict-Transport-Security:max-age=30;includeSubDomains
 X-Frame-Options:SAMEORIGIN
 X-XSS-Protection:1; mode=block
 此时,使用浏览器访问页面时,图片可以正常呈现。 
- 修改$CATALINA_BASE/conf/web.xml,恢复X-Content-Type-Options特性<filter>
 <filter-name>httpHeaderSecurity</filter-name>
 <filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
 <async-supported>true</async-supported>
 </filter>
 修改 GetPictureServlet类的实现,写入图片流前先设置HTTP响应头部Content-Type,取值为image/pngresp.setHeader("Content-Type", "image/png");
 使用浏览器的调试面板观察Tomcat响应浏览器请求时的HTTP头部 Cache-Control:private
 Content-Length:5103
 Content-Type:image/png
 Date:Thu, 05 May 2016 15:38:12 GMT
 Expires:Thu, 01 Jan 1970 08:00:00 CST
 Server:Apache-Coyote/1.1
 Strict-Transport-Security:max-age=30;includeSubDomains
 X-Content-Type-Options:nosniff
 X-Frame-Options:SAMEORIGIN
 X-XSS-Protection:1; mode=block
 此时,使用浏览器访问页面时,图片可以正常呈现。 
结论
按照减少 MIME 类型的安全风险的介绍,IE的行为受X-Content-Type-Options的影响,如果Web应用没有返回Content-Type,那么IE9、IE11将拒绝加载相关资源。
如果服务器发送响应头 “X-Content-Type-Options: nosniff”,则 script 和 styleSheet 元素会拒绝包含错误的 MIME 类型的响应。这是一种安全功能,有助于防止基于 MIME 类型混淆的攻击。
从前述的测试结果看,的确证实了X-Content-Type-Options对IE9、IE11的影响。
但仍有不明之事,文章减少 MIME 类型的安全风险只提到了script和stylesheet标签,没有提到img标签,不了解为什么X-Content-Type-Options对图片的加载也会产生影响。后来使用Windows 10的Edge做验证,发现Edge也存在相同的问题,只要启用了X-Content-Type-Options,那么图片必定呈现不出来。Jackie猜,这也许是文档太旧了,或者是Jackie没有找到最新的文档。
另外按照Jerry Qu的文章一些安全相关的HTTP响应头的描述,X-Content-Type-Options应该会影响Chrome的行为,但从测试结果看,使用img标签加载图片时,如果Web应用没有返回Content-Type,无论是否启用X-Content-Type-Options,Chrome都可以正常呈现图片,这一点比较奇怪。
参考资料
HTTP协议安全头部X-Content-Type-Options引入的问题的更多相关文章
- Jsoup问题---获取http协议请求失败 org.jsoup.UnsupportedMimeTypeException: Unhandled content type. Must be text/*, application/xml, or application/xhtml+xml.
		Jsoup问题---获取http协议请求失败 1.问题:用Jsoup在获取一些网站的数据时,起初获取很顺利,但是在访问某浪的数据是Jsoup报错,应该是请求头里面的请求类型(ContextType)不 ... 
- org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported或其他Content type不支持处理
		很久没从头到尾搭框架,今天搭的过程中,springmvc controller方法入参用@RequestBody自动绑定参数时一直提示各种 not supported 排查问题有两个解决路径: 1)使 ... 
- Jsoup获取部分页面数据失败 org.jsoup.UnsupportedMimeTypeException: Unhandled content type. Must be text/*, application/xml, or application/xhtml+xml.
		用Jsoup在获取一些网站的数据时,起初获取很顺利,但是在访问某浪的数据是Jsoup报错,应该是请求头里面的请求类型(ContextType)不符合要求. 请求代码如下: private static ... 
- SharePoint自动化系列——Add content type to list.
		转载请注明出自天外归云的博客园:http://www.cnblogs.com/LanTianYou/ 将创建好的content type(若是跨web application需要事先publish c ... 
- SharePoint自动化系列——Content Type相关timer jobs一键执行
		转载请注明出自天外归云的博客园:http://www.cnblogs.com/LanTianYou/ 背景: 在SharePoint Central Administration->Monito ... 
- 转载 SharePoint【Site Definition 系列】– 创建Content Type
		转载原地址: http://www.cnblogs.com/wsdj-ITtech/archive/2012/09/01/2470274.html Sharepoint本身就是一个丰富的大容器,里面 ... 
- the request doesn't contain a multipart/form-data or multipart/form-data stream, content type header
		the request doesn't contain a multipart/form-data or multipart/form-data stream, content type header ... 
- Springs Element 'beans' cannot have character [children], because the type's content type is element-only
		Springs Element 'beans' cannot have character [children], because the type's content type is element ... 
- springboot 报错 Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported
		开始 controller 方法写的是 @RequestMapping( value = "/add", method = RequestMethod.POST ) public ... 
- .NET获取文件的MIME类型(Content Type)
		第一种:这种获取MIME类型(Content Type)的方法需要在.NET 4.5之后才能够支持,但是非常简单. 优点:方便快捷 缺点:只能在.NET 4.5之后使用 public FileResu ... 
随机推荐
- sharepoint_study_2
			描述:向SharePoint中批量添加用户 解决:原文地址:http://bbs.winos.cn/thread-89236-1-1.html 一般情况下,要想登录SharePoint server ... 
- 在Eclipse中调试web项目
			由于现在的公司用的是Eclipse开发web项目而且不安装MyEclipse插件,没有myclipse插件就不能在Eclipse中配置web服务器,所以也就不好对web项目进行调试.下面的方法就可以让 ... 
- C++ GUI Qt4编程(13)-6.2preferencedialog
			1. 主要介绍了QStackedLayout.QListWidget.QDialogButtonBox的简单用法.2. QStackedLayout: 要使某个特定的子窗口部件可见,可以用setC ... 
- tomcat普通用户启动不了
			Neither the JAVA_HOME nor the JRE_HOME environment variable is defined At least one of these enviro ... 
- js简介  基本操作 以及循环语句 内置对象 函数044
			js 全称 javascript 从交互的角度 描述行为 一 .js注释方法: //单行注释 声明变量 var 二 .声明多个变量 : var a = '2' ,b = 4, c = tru ... 
- python小商店
			(1) 输入自己所有的钱.(2) 展示商品的序号,名称及其价格.(3) 输入要买商品的序号.(4) 输入要买商品的数量.(5) 购物车中显示购买的水果名称及其对应的数量和剩余钱.(6) 如果序号输入有 ... 
- V1-Team Scrum Meeting 博客汇总
			V1-Team Scrum Meeting 博客汇总 计划文档 功能规格说明书 技术规格说明书 项目分解 贡献分配规则 一.Alpha阶段 第一次 Scrum Meeting 第二次 Scrum Me ... 
- 采用MQTT协议实现android消息推送(1)MQTT 协议简介
			1.资料 mqtt官网 http://mqtt.org/ 服务端程序列表 https://github.com/mqtt/mqtt.github.io/wiki/servers 客户端库列表 http ... 
- 通过Qt从URL下载文件
			示例1: 通过Qt自带的例子学习,位置:[安装盘符]:\Qt\Qt5.1.1\5.1.1\Src\qtbase\examples\network\download 示例2: 通过Qt的文档,位置: ... 
- Python编程:基础学习常见错误整理
			# Python学习之错误整理: # 错误一:# TypeError: cannot concatenate 'str' and 'int' objects# 不能连接str和int对象age = 2 ... 
