Spring3.x經過配置來防範csrf攻擊

1. [代碼]CsrfTokenManager 用於管理csrfToken相關     

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
packagecom.uncle5.pubrub.web.common;
 
importjava.util.UUID;
 
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpSession;
 
publicfinalclassCsrfTokenManager {
 
    // 隱藏域參數名稱
    staticfinalString CSRF_PARAM_NAME ="CSRFToken";
 
    // session中csrfToken參數名稱
    publicstaticfinalString CSRF_TOKEN_FOR_SESSION_ATTR_NAME = CsrfTokenManager.class
            .getName() +".tokenval";
 
    privateCsrfTokenManager() {
    };
 
    // 在session中建立csrfToken
    publicstaticString createTokenForSession(HttpSession session) {
        String token =null;
 
        synchronized(session) {
            token = (String) session
                    .getAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME);
            if(null== token) {
                token = UUID.randomUUID().toString();
                session.setAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME, token);
            }
        }
        returntoken;
    }
 
    publicstaticString getTokenFromRequest(HttpServletRequest request) {
        returnrequest.getParameter(CSRF_PARAM_NAME);
    }
}

2. [代碼]CsrfRequestDataValueProcessor 自動建立hidden的csrfToken域的類     

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
packagecom.uncle5.pubrub.web.common;
 
importjava.util.Map;
 
importjavax.servlet.http.HttpServletRequest;
 
importorg.springframework.stereotype.Component;
importorg.springframework.web.servlet.support.RequestDataValueProcessor;
 
importcom.google.common.collect.Maps;
 
@Component("requestDataValueProcessor")
publicclassCsrfRequestDataValueProcessorimplementsRequestDataValueProcessor {
 
    @Override
    publicString processAction(HttpServletRequest request, String action) {
        // TODO 暫時原樣返回action
        returnaction;
    }
 
    @Override
    publicString processFormFieldValue(HttpServletRequest request,
            String name, String value, String type) {
        // TODO 暫時原樣返回value
        returnvalue;
    }
 
    @Override
    publicMap<String, String> getExtraHiddenFields(HttpServletRequest request) {
        //此處是當使用spring的taglib標籤<form:form>建立表單時候,增長的隱藏域參數
        Map<String, String> hiddenFields = Maps.newHashMap();
        hiddenFields.put(CsrfTokenManager.CSRF_PARAM_NAME,
                CsrfTokenManager.createTokenForSession(request.getSession()));
 
        returnhiddenFields;
    }
 
    @Override
    publicString processUrl(HttpServletRequest request, String url) {
        // TODO 暫時原樣返回url
        returnurl;
    }
 
}

3. [代碼]CsrfInterceptor 對於post請求進行攔截,檢測csrfToken是否匹配     

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
packagecom.uncle5.pubrub.web.security;
 
importjava.net.URLEncoder;
 
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
 
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.util.StringUtils;
importorg.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 
importcom.uncle5.pubrub.web.common.CsrfTokenManager;
importcom.uncle5.pubrub.web.common.WebUser;
 
publicclassCsrfInterceptorextendsHandlerInterceptorAdapter {
 
    privatestaticfinalLogger logger = LoggerFactory
            .getLogger(CsrfInterceptor.class);
 
    @Autowired
    WebUser webUser;
 
    @Override
    publicbooleanpreHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler)throwsException {
 
        if("POST".equalsIgnoreCase(request.getMethod())) {
            String CsrfToken = CsrfTokenManager.getTokenFromRequest(request);
            if(CsrfToken ==null
                    || !CsrfToken.equals(request.getSession().getAttribute(
                            CsrfTokenManager.CSRF_TOKEN_FOR_SESSION_ATTR_NAME))) {
                String reLoginUrl ="/login?backurl="
                        + URLEncoder.encode(getCurrentUrl(request),"utf-8");
 
                response.sendRedirect(reLoginUrl);
                returnfalse;
            }
        }
 
        returntrue;
    }
 
    privateString getCurrentUrl(HttpServletRequest request) {
        String currentUrl = request.getRequestURL().toString();
        if(!StringUtils.isEmpty(request.getQueryString())) {
            currentUrl +="?"+ request.getQueryString();
        }
 
        returncurrentUrl;
    }
}

4. [代碼]springMVC 配置文件,增長鬚要進行攔截的url     

?
1
2
3
4
5
6
7
8
<!-- 安全攔截用的 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mappingpath="/forum/post"/>
            <mvc:mappingpath="/forum/reply/*"/>
            <beanclass="com.uncle5.pubrub.web.security.CsrfInterceptor"/>
        </mvc:interceptor>       
    </mvc:interceptors>

5. [代碼]jsp頁面,須要注意的是必須使用spring的form標籤     

?
1
2
3
4
5
6
7
8
9
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
 
<divstyle="margin: 0 auto; border: 1px solid orange; width: 80%; height: 400px;">
        <form:formaction="/forum/post"method="post">
            <span>標題:</span><inputtype="text"name="title"id="title">
            <textareaname="content"id="content"rows="30"cols="40"></textarea>
            <inputtype="submit"name="submit"value="submit">
        </form:form>
    </div>

6. [代碼]jsp頁面 查看源碼會發現已經添加上了hidden字段     

?
1
2
3
4
5
6
7
8
<divstyle="margin: 0 auto; border: 1px solid orange; width: 80%; height: 400px;">
        <formid="command"action="/forum/post"method="post">
            <span>標題:</span><inputtype="text"name="title"id="title">
            <textareaname="content"id="content"rows="30"cols="40"></textarea>          
            <inputtype="submit"name="submit"value="submit">
        <inputtype="hidden"name="CSRFToken"value="35afec82-da7b-449e-9ae9-b38664b5af63"/>
</form>
    </div>

7. [代碼]相關原理說明     

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1.只有當使用spring的form標籤時候,纔會在渲染jsp頁面時候觸發
org.springframework.web.servlet.tags.form.FormTag 類中的
 
@Override
    publicintdoEndTag()throwsJspException {
        RequestDataValueProcessor processor = getRequestContext().getRequestDataValueProcessor();
        ServletRequest request =this.pageContext.getRequest();
        if((processor !=null) && (requestinstanceofHttpServletRequest)) {
            writeHiddenFields(processor.getExtraHiddenFields((HttpServletRequest) request));
        }
        this.tagWriter.endTag();
        returnEVAL_PAGE;
    }
該方法會調用上文中所說的CsrfRequestDataValueProcessor中的建立隱藏域的方法getExtraHiddenFields來建立csrfToken隱藏域。
 
2.對相關須要進行防範csrf攻擊的post請求地址進行攔截,此處是依賴springMVC中提供的攔截器機制來操做的
<mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/forum/post"/>
            <mvc:mapping path="/forum/reply/*"/>
            <beanclass="com.uncle5.pubrub.web.security.CsrfInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
當用戶訪問"/forum/post"這個請求時候,會直接進入CsrfInterceptor的preHandle方法,此處針對post請求

1. [代碼]CsrfTokenManager 用於管理csrfToken相關     

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
packagecom.uncle5.pubrub.web.common;
 
importjava.util.UUID;
 
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpSession;
 
publicfinalclassCsrfTokenManager {
 
    // 隱藏域參數名稱
    staticfinalString CSRF_PARAM_NAME ="CSRFToken";
 
    // session中csrfToken參數名稱
    publicstaticfinalString CSRF_TOKEN_FOR_SESSION_ATTR_NAME = CsrfTokenManager.class
            .getName() +".tokenval";
 
    privateCsrfTokenManager() {
    };
 
    // 在session中建立csrfToken
    publicstaticString createTokenForSession(HttpSession session) {
        String token =null;
 
        synchronized(session) {
            token = (String) session
                    .getAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME);
            if(null== token) {
                token = UUID.randomUUID().toString();
                session.setAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME, token);
            }
        }
        returntoken;
    }
 
    publicstaticString getTokenFromRequest(HttpServletRequest request) {
        returnrequest.getParameter(CSRF_PARAM_NAME);
    }
}

2. [代碼]CsrfRequestDataValueProcessor 自動建立hidden的csrfToken域的類     

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
packagecom.uncle5.pubrub.web.common;
 
importjava.util.Map;
 
importjavax.servlet.http.HttpServletRequest;
 
importorg.springframework.stereotype.Component;
importorg.springframework.web.servlet.support.RequestDataValueProcessor;
 
importcom.google.common.collect.Maps;
 
@Component("requestDataValueProcessor")
publicclassCsrfRequestDataValueProcessorimplementsRequestDataValueProcessor {
 
    @Override
    publicString processAction(HttpServletRequest request, String action) {
        // TODO 暫時原樣返回action
        returnaction;
    }
 
    @Override
    publicString processFormFieldValue(HttpServletRequest request,
            String name, String value, String type) {
        // TODO 暫時原樣返回value
        returnvalue;
    }
 
    @Override
    publicMap<String, String> getExtraHiddenFields(HttpServletRequest request) {
        //此處是當使用spring的taglib標籤<form:form>建立表單時候,增長的隱藏域參數
        Map<String, String> hiddenFields = Maps.newHashMap();
        hiddenFields.put(CsrfTokenManager.CSRF_PARAM_NAME,
                CsrfTokenManager.createTokenForSession(request.getSession()));
 
        returnhiddenFields;
    }
 
    @Override
    publicString processUrl(HttpServletRequest request, String url) {
        // TODO 暫時原樣返回url
        returnurl;
    }
 
}

3. [代碼]CsrfInterceptor 對於post請求進行攔截,檢測csrfToken是否匹配     

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
packagecom.uncle5.pubrub.web.security;
 
importjava.net.URLEncoder;
 
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
 
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.util.StringUtils;
importorg.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 
importcom.uncle5.pubrub.web.common.CsrfTokenManager;
importcom.uncle5.pubrub.web.common.WebUser;
 
publicclassCsrfInterceptorextendsHandlerInterceptorAdapter {
 
    privatestaticfinalLogger logger = LoggerFactory
            .getLogger(CsrfInterceptor.class);
 
    @Autowired
    WebUser webUser;
 
    @Override
    publicbooleanpreHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler)throwsException {
 
        if("POST".equalsIgnoreCase(request.getMethod())) {
            String CsrfToken = CsrfTokenManager.getTokenFromRequest(request);
            if(CsrfToken ==null
                    || !CsrfToken.equals(request.getSession().getAttribute(
                            CsrfTokenManager.CSRF_TOKEN_FOR_SESSION_ATTR_NAME))) {
                String reLoginUrl ="/login?backurl="
                        + URLEncoder.encode(getCurrentUrl(request),"utf-8");
 
                response.sendRedirect(reLoginUrl);
                returnfalse;
            }
        }
 
        returntrue;
    }
 
    privateString getCurrentUrl(HttpServletRequest request) {
        String currentUrl = request.getRequestURL().toString();
        if(!StringUtils.isEmpty(request.getQueryString())) {
            currentUrl +="?"+ request.getQueryString();
        }
 
        returncurrentUrl;
    }
}

4. [代碼]springMVC 配置文件,增長鬚要進行攔截的url     

?
1
2
3
4
5
6
7
8
<!-- 安全攔截用的 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mappingpath="/forum/post"/>
            <mvc:mappingpath="/forum/reply/*"/>
            <beanclass="com.uncle5.pubrub.web.security.CsrfInterceptor"/>
        </mvc:interceptor>       
    </mvc:interceptors>

5. [代碼]jsp頁面,須要注意的是必須使用spring的form標籤     

?
1
2
3
4
5
6
7
8
9
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
 
<divstyle="margin: 0 auto; border: 1px solid orange; width: 80%; height: 400px;">
        <form:formaction="/forum/post"method="post">
            <span>標題:</span><inputtype="text"name="title"id="title">
            <textareaname="content"id="content"rows="30"cols="40"></textarea>
            <inputtype="submit"name="submit"value="submit">
        </form:form>
    </div>

6. [代碼]jsp頁面 查看源碼會發現已經添加上了hidden字段     

?
1
2
3
4
5
6
7
8
<divstyle="margin: 0 auto; border: 1px solid orange; width: 80%; height: 400px;">
        <formid="command"action="/forum/post"method="post">
            <span>標題:</span><inputtype="text"name="title"id="title">
            <textareaname="content"id="content"rows="30"cols="40"></textarea>          
            <inputtype="submit"name="submit"value="submit">
        <inputtype="hidden"name="CSRFToken"value="35afec82-da7b-449e-9ae9-b38664b5af63"/>
</form>
    </div>

7. [代碼]相關原理說明     

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1.只有當使用spring的form標籤時候,纔會在渲染jsp頁面時候觸發
org.springframework.web.servlet.tags.form.FormTag 類中的
 
@Override
    publicintdoEndTag()throwsJspException {
        RequestDataValueProcessor processor = getRequestContext().getRequestDataValueProcessor();
        ServletRequest request =this.pageContext.getRequest();
        if((processor !=null) && (requestinstanceofHttpServletRequest)) {
            writeHiddenFields(processor.getExtraHiddenFields((HttpServletRequest) request));
        }
        this.tagWriter.endTag();
        returnEVAL_PAGE;
    }
該方法會調用上文中所說的CsrfRequestDataValueProcessor中的建立隱藏域的方法getExtraHiddenFields來建立csrfToken隱藏域。
 
2.對相關須要進行防範csrf攻擊的post請求地址進行攔截,此處是依賴springMVC中提供的攔截器機制來操做的
<mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/forum/post"/>
            <mvc:mapping path="/forum/reply/*"/>
            <beanclass="com.uncle5.pubrub.web.security.CsrfInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
當用戶訪問"/forum/post"這個請求時候,會直接進入CsrfInterceptor的preHandle方法,此處針對post請求
相關文章
相關標籤/搜索