HTTP協議安全頭部X-Content-Type-Options引入的問題
原文地址:http://www.jackieathome.net/archives/369.html?utm_source=tuicool&utm_medium=referral
前段時間測試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/png
resp.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都可以正常呈現圖片,這一點比較奇怪。