您的位置:首页 > 其它

CSRF攻击防范

2016-09-01 10:17 274 查看
 CSRF全称:(Cross-site request forgery)跨域请求伪造

理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求

对于CSRF概念的理解:http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html

以下是自己的总结与实现方式(拦截器实现):

CSRF攻击必须依次完成以下两个条件:

 1.登录受信任网站A,并在本地生成Cookie。

  2.在不登出A的情况下,访问危险网站B。

关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了

CSRF攻击形成的原因:

1.网站违反了HTTP协议,使用GET请求更新资源(非常危险).

2.使用无校验的表单

CSRF攻击是源于WEB的隐式身份验证机制!(比如你在A网站登录后,在同一个浏览器的tab页打开B网站网页, 而B网站网页有一些脚本链接到A网站并偷偷的发送请求更新你账号的信息.如果没有防CSRF攻击,这样是可以实现的.)

WEB的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的!

CSRF防范方式:客户端页面增加伪随机数。每次需要修改该用户的数据库信息时必须带上该随机数,否则不受理.

解决办法: 在Form表单加一个hidden field,里面是服务端生成的足够随机数的一个Token(恶意网站猜不到也无法获取到相同的Token), 然后使用一个拦截器interceptor来检查每一个非get请求, 看该token与服务器token是否一致,不一致的不受理该请求.

以下是token 的工具管理类:

package com.xxx.xxx.util;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.UUID;

/**
* CSRFtoken管理类
*
* @author 作者  yss
* @version 版本号 v1.0
*/
public final class CSRFTokenManager {

/**
* 约定规范:表单提交时的token的input的name属性必须为该值才能获取到后台返回的token。
* 下面是使用EL表达式接收从后台返回的token参数
* 如: <input type="hidden" id="CSRFToken" value="${CSRFToken}">
*/
public static final String CSRF_PARAM_NAME = "CSRFToken";

/**
* 存放在session中的token名称(跟上面的name属性值不一定一样)
*/
public static final String CSRF_TOKEN_FOR_SESSION_ATTR_NAME = CSRFTokenManager.class.getName() + ".tokenval";

/**
*从session中获取token字符串
* @param session
* @return
*/
public static String getTokenForSession(HttpSession session) {
String token = null;
//保证session只存在一个token,避免多线程情况下产生冲突。
synchronized (session) {
token = (String) session.getAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME);//尝试获取session中的token
if (null == token) {//如果session中没有token,就重新生成一个token
token = UUID.randomUUID().toString();
session.setAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME, token);
}
}
return token;
}

/**
*获取到request中的token值。
* @param request
* @return
*/

public static String getTokenFromRequest(HttpServletRequest request) {
return request.getParameter(CSRF_PARAM_NAME);
}
//构造器
private CSRFTokenManager() {
}
}


后台返回token参数给页面:

String token = CSRFTokenManager.getTokenForSession(request.getSession());//uuid生成的随机token
modelAndView.addObject(CSRFTokenManager.CSRF_PARAM_NAME, token);//添加token参数


页面使用隐藏域保存起来:

<input type="hidden" id="CSRFToken" value="${CSRFToken}"><%--token--%>


编写代码要符合HTTP规范,不要使用GET请求更新资源

在非GET请求中带上token 参数(ajax请求也要),然后使用拦截器检查.

(对于受到CSRF攻击我使用的是抛自定义异常,然后使用springmvc全局异常进行处理,您如果想了解springmvc全局异常处理可以看我博客.)

package com.xxx.xxx.interceptor;

import com.xxx.xxx.exception.CSRFException;
import com.xxx.xxx.util.CSRFTokenManager;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* 对于未登录用户的请求进行拦截,确保用户已经登录,然后进行后续的网页请求
*
* @Author yss
* @Version 1.0
* @see
*/
public class CSRFInterceptor extends HandlerInterceptorAdapter {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//        Enumeration parasm = request.getParameterNames();
if (!"GET".equals(request.getMethod())) {//非get请求
String CSRFToken = CSRFTokenManager.getTokenFromRequest(request);//页面传过来的csrf参数
if (CSRFToken == null || !CSRFToken.equals(CSRFTokenManager.getTokenForSession(request.getSession()))) {//token不对应
throw new CSRFException("CSRF攻击");//抛异常后将会进入springmvc全局异常处理体系
}
}
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
super.postHandle(request, response, handler, modelAndView);
}
}


在spring的配置文件中添加拦截器使其生效

<mvc:interceptors>
<!-- 防止CSRF攻击的拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean id="CSRFInterceptor" class="com.xxx.xxx.interceptor.CSRFInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>


有问题欢迎留言. 相互探讨相互学习.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息