Spring3.x通过配置来防范csrf攻击
Spring3.x通过配置来防范csrf攻击
java梦想家01 发表于2年前
Spring3.x通过配置来防范csrf攻击
  • 发表于 2年前
  • 阅读 1776
  • 收藏 10
  • 点赞 0
  • 评论 0

腾讯云 技术升级10大核心产品年终让利>>>   

摘要: 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请求
标签: springmvc csfr
共有 人打赏支持
粉丝 12
博文 60
码字总数 14212
×
java梦想家01
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: