xss攻擊原理
用戶 使用瀏覽器訪問可信的站點A進行業務,此時瀏覽器會保存站點A相關的cookie
用戶 使用瀏覽器訪問一個惡意的站點B,如果站點B中的網頁具有指向站點A的鏈接,攻擊就有可能發生。有如下幾種情況:
b、站點B返回給用戶的頁面包含<img src='XXX'>,其中XXX就是指向站點A的鏈接,這樣用戶只要訪問站點B的頁面就被攻擊了,因爲瀏覽器會解析<img src='XXX'>標籤,自動獲取XXX的’圖片內容‘
c、站點B返回給用戶的頁面包含自動加載的js,且js中有跳轉到站點A的動作,同上,用戶只要訪問站點B就被攻擊了
a、b、c三總跳轉的方式不同但是利用的原理是一樣的,那就是用戶在本地還保存這website1的cookie,再次通過website2上的鏈接請求站點A時,瀏覽器就會自動將站點A對應的cookie加上,這樣站點A就會認爲這個請求是用戶合法請求而進行處理。
站點B上的攻擊者就可以通過編寫特定的請求內容,進行攻擊
xss防範
在Tomcat中,默認提供了一個防範CSRF的好工具: CSRF Prevention Filter 。
Tomcat默認提供了各類的Filter,處理不同的場景和需求。像我們前面介紹過的處理編碼的 Tomcat自帶的設置編碼Filter , 還有進行跨域處理的 Tomcat與跨域問題 等等。今天介紹的CSRF Prevention Filter也是其中的一個。
整個Filter的工作流程可以概括成以下內容:
該Filter爲Web應用提供了基本的CSRF 保護。它的filter mapping對應到 /*
並且所有返回到頁面上的鏈接,都通過調用 HttpServletResponse # encodeRedirectURL(String) 或者 HttpServletResponse # encodeURL(String) 進行編碼。實現機制是生成一個token並且將其保存到session中,URL的encode也使用同樣的token,當請求到達時,會比較請求中的token和session中的token是否一致,只有相同的才允許繼續執行。
我們通過一個例子,深入源碼,來了解下內部的實現細節。
還是使用Tomcat自帶的Manager應用來看下。
在其 web.xml 中,有這樣的配置:
<filter>
<filter-name>CSRF</filter-name>
<filter-class>org.apache.catalina.filters.CsrfPreventionFilter</filter-class>
<init-param>
<param-name>entryPoints</param-name>
<param-value>/index/index.xhtml</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CSRF</filter-name>
<url-pattern>/index/index.xhtml</url-pattern>
</filter-mapping>
下面的內容是CsrfPreventionFilter的 doFilter 方法, @Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
ServletResponse wResponse = null;
if (request instanceof HttpServletRequest &&
response instanceof HttpServletResponse) {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
boolean skipNonceCheck = false;
if (Constants.METHOD_GET.equals(req.getMethod())) {
String path = req.getServletPath();
if (req.getPathInfo() != null) {
path = path + req.getPathInfo();
}
if (entryPoints.contains(path)) {
skipNonceCheck = true;
}
}
HttpSession session = req.getSession(false);
@SuppressWarnings("unchecked")
LruCache<String> nonceCache = (session == null) ? null
: (LruCache<String>) session.getAttribute(
Constants.CSRF_NONCE_SESSION_ATTR_NAME);
if (!skipNonceCheck) {
String previousNonce =
req.getParameter(Constants.CSRF_NONCE_REQUEST_PARAM);
if (nonceCache == null || previousNonce == null ||
!nonceCache.contains(previousNonce)) {
res.sendError(denyStatus);
return;
}
}
if (nonceCache == null) {
nonceCache = new LruCache<>(nonceCacheSize);
if (session == null) {
session = req.getSession(true);
}
session.setAttribute(
Constants.CSRF_NONCE_SESSION_ATTR_NAME, nonceCache);
}
String newNonce = generateNonce();
nonceCache.add(newNonce);
wResponse = new CsrfResponseWrapper(res, newNonce);
} else {
wResponse = response;
}
chain.doFilter(request, wResponse);
}
以上,就是CSRF Prevetion Filter實現的原理和細節。當然,上面返回403的地方,以及生成nonce的地方,都可以通過Filter提供的參數來進行配置,分別對應到denyStatus和randomClass。後者需要提供一個Random的實現