跨站腳本攻擊(XSS)可以讓攻擊者在受害者的瀏覽器中執行惡意腳本來修改網頁內容、將用戶重定向到非法網站、偽造用戶登錄態、竊取用戶的隱私信息、甚至還能給程序開個后門等等,所以不得不防。今天就來分享幾種常用的防范XSS攻擊的措施。
XSS攻擊
可能上面說的不夠直觀,下面我們來看一下XSS攻擊的方式。假設我們寫了一個注冊用戶接口:
- POST /user
- Host: api.felord.cn
- {
- "userId" : 1001,
- "username" : "felord.cn",
- "type" : "\<script\>alert(document.cookie)\</script\>"
- }
這樣的用戶如果查詢出來并被渲染到前端時,type字段的值很容易被當做腳本執行,這是就是一種常見的XSS攻擊。胖哥在剛剛入行的時候就遇到過,有人利用XSS掛他自己的廣告到我們的網站中來牟取利益。我們需要在應用中做一些防御措施。
防范XSS攻擊的手段
下面就是我比較常用的手段。
X-XSS-Protection請求頭
X-XSS-Protection 響應頭是 IE,Edge,Chrome 和 Safari 的一個特性,當檢測到跨站腳本攻擊 (XSS) 時,瀏覽器將停止加載頁面。
- # 0 表示禁止XSS過濾 1 表示開啟XSS過濾
- X-XSS-Protection: 0
- X-XSS-Protection: 1
- # 啟用XSS過濾。 如果檢測到攻擊,瀏覽器將不會清除頁面,而是阻止頁面加載。
- X-XSS-Protection: 1; mode=block
- # 啟用XSS過濾 (谷歌瀏覽器專用)。 如果檢測到跨站腳本攻擊,瀏覽器將清除頁面并使用CSP report-uri指令的功能發送違規報告。
- X-XSS-Protection: 1; report=<reporting-uri>
大部分瀏覽器都支持這一特性。
瀏覽器兼容性
可以看得出X-XSS-Protection的兼容性還是很好的,不過它的保護性比較弱。默認情況下,Spring security 會自動添加此請求頭。
CSP請求頭
上面已經提到了CSP,全稱Content-Security-Policy(內容安全策略),它也是以請求頭的形式存在。它允許站點管理者控制用戶代理能夠為指定的頁面加載哪些資源。除了少數例外情況,設置的政策主要涉及指定服務器的源和腳本結束點。這將幫助防止跨站腳本攻擊(XSS)。它的控制粒度更細,它通過一系列的指令聲明可以決定URL、多媒體資源、字體的加載策略、腳本的執行策略。具體可以查看Content-Security-Policy文檔。
例如僅支持執行來自https://felord.cn的腳本:
- Content-Security-Policy: script-src https://felord.cn
目前主流的瀏覽器也都支持該特性。
支持CSP的瀏覽器
在Spring Security中我們可以這樣配置它:
- httpSecurity.headers()
- .contentSecurityPolicy(“script-src https://felord.cn”)
編碼過濾轉義
除此之外我們還可以使用編碼的形式來轉義請求參數和響應體的字符來防止XSS攻擊。這里會用到Spring提供的工具類org.springframework.web.util.HtmlUtils,當然Apache Commons也有類似的工具類。
- HtmlUtils.htmlEscape(String value)
利用上面這個方法我們可以針對性的編寫HttpServletRequestWrapper的實現來對請求參數進行轉義:
- import org.springframework.web.util.HtmlUtils;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletRequestWrapper;
- import java.util.stream.Stream;
- public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
- public XssHttpServletRequestWrapper(HttpServletRequest request) {
- super(request);
- }
- @Override
- public String getHeader(String name) {
- String value = super.getHeader(name);
- return HtmlUtils.htmlEscape(value);
- }
- @Override
- public String getParameter(String name) {
- String value = super.getParameter(name);
- return HtmlUtils.htmlEscape(value);
- }
- @Override
- public String[] getParameterValues(String name) {
- String[] values = super.getParameterValues(name);
- return values != null ? (String[]) Stream.of(values)
- .map(HtmlUtils::htmlEscape).toArray() :
- super.getParameterValues(name);
- }
- }
結合 Servlet Filter 或者Spring MVC 攔截器。
編寫JSON序列化來實現對JSON返回的轉義,例如Jackson中自定義XSS序列化
- public class XssStringJsonSerializer extends JsonSerializer<String> {
- @Override
- public Class<String> handledType() {
- return String.class;
- }
- @Override
- public void serialize(String value, JsonGenerator jsonGenerator,
- SerializerProvider serializerProvider) throws IOException {
- if (value != null) {
- jsonGenerator.writeString(HtmlUtils.htmlEscape(value));
- }
- }
- }
總結
今天介紹了幾種常用的防止XSS攻擊的方式,主要是涉及后端的。其實像一些現代的前端框架都支持將字符串變量轉義,比如React的JSX。不過話又說回來,提高應用的安全的根本方法就在于降低攻擊者的收益和提高攻擊者的成本。
原文鏈接:https://mp.weixin.qq.com/s/J4IFzCQlM5G0IyIFsqZz5g