CSRF的攻擊和防範

xss攻擊原理

用戶 使用瀏覽器訪問可信的站點A進行業務,此時瀏覽器會保存站點A相關的cookie

用戶 使用瀏覽器訪問一個惡意的站點B,如果站點B中的網頁具有指向站點A的鏈接,攻擊就有可能發生。有如下幾種情況:


a、站點B返回給用戶的頁面包含站點A的鏈接,點擊這個鏈接就會跳轉到站點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的實現
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章