使用java方式处理跨站请求伪造(CSRF)

1 篇文章 0 订阅
1 篇文章 0 订阅

什么是CSRF

	跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。

跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。

因此一些表单请求如果存在CSRF漏洞,那么攻击者将构造相应请求的链接诱导管理员点击,可以在用户不知情的情况下进行某些敏感操作。

那么我们应该怎么处理呢?

通过检查Referer字段来进行预防

HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer字段应和请求的地址位于同一域名下。以本地项目操作为例,Referer字段地址通常应该是登录按钮所在的来源网页地址,应该也位于http://localhost:8081/xxx/之下。而如果是CSRF攻击传来的请求,Referer字段会是包含恶意网址的地址,不会位于http://localhost:8081/xxx/之下,这时候服务器就能识别出恶意的访问。
在请求头里有referer字段
当我们通过网站进行正常访问请求时,在请求头里都会有referer字段,但是当我们在地址栏里进行访问时就不会有referer字段,那么我们可以根据浏览器这样的特性去预防CSRF攻击
例如我们可以编写过滤器来进行防御

/**
 * @ClassName: UrlFilter
 * 		统一处理CSRF伪造路径
 * @Desc: TODO
 * @author: qsh
 * @date: 2019年8月6日 下午15:11:56
 * @version 1.0
 */
public class UrlFilter implements Filter{
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        System.out.println("进入CSRF拦截器");
        String referer = request.getHeader("referer");
        if (referer != null && referer.trim().startsWith("http://localhost:8081/")){
            System.out.println("referer不为空并且路径符合系统要求");
            filterChain.doFilter(request,servletResponse);
        }else if(referer != null && referer.trim().startsWith("http://IP地址/")){
            filterChain.doFilter(request,servletResponse);
        }else {
            System.out.println("referer可能为空,或者系统遭受到CSRF攻击!");
//            request .getRequestDispatcher("/error.jsp").forward(request ,response);
//          这里写你想处理的逻辑,我的处理方式是重定向到首页面,你也可以转发到error.jsp
            response.sendRedirect(BaseInfo.getContextPath() + "/manager/index.do");
        }
    }

    @Override
    public void destroy() {

    }
}

这种办法简单易行,工作量低,虽然可以预防CSRF攻击,仅需要在关键访问处增加一步校验。但这种办法也有其局限性,不够稳定,因其完全依赖浏览器发送正确的Referer字段。虽然http协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其Referer字段的可能。

那么我们就要增添第二步保证了!

添加校验token

由于CSRF的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在cookie中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再执行CSRF攻击。这种数据通常是表单中的一个数据项。服务器将其生成并附加在表单中,其内容是一个伪乱数。当客户端通过表单提交请求时,这个伪乱数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪乱数,而通过CSRF传来的欺骗性攻击中,攻击者无从事先得知这个伪乱数的值,服务器端就会因为校验token的值为空或者错误,拒绝这个可疑请求。

public class CsrfDefInterceptor extends BaseInterceptor {

	public final static String TOKEN = "csrf_token";

	public final static String REDIRECT_URL = "csrf_redirecturl";

	/**
	 * 增加csrf拦截标志,csrf的请求跳转地址为 应用路径
	 * 
	 * @param response
	 * @param session
	 */
	public static void addCsrfToken(HttpServletResponse response, HttpSession session) {
		addCsrfToken(response, session, null);
	}

	/**
	 * 增加csrf拦截标志
	 * 
	 * @param response
	 * @param session
	 * @param redirectUrl
	 *            csrf的请求跳转地址
	 */
	public static void addCsrfToken(HttpServletResponse response, HttpSession session, String redirectUrl) {
		// 加入标志给dologin判断是否是csrf  
		//ps:这里使用的是公司内封装的jar,因此不好放出来,下面我会对其重新封装改造下
		String token = Md5Util.encode(StringUtil.getUUIDString());
		session.setAttribute(CsrfDefInterceptor.TOKEN, token);
		session.setAttribute(CsrfDefInterceptor.REDIRECT_URL, redirectUrl);
		ControllerUtil.addCookie(response, CsrfDefInterceptor.TOKEN, token, 60 * 60 * 24 * 365);
	}

	@Override
	public void after(HttpServletRequest req, HttpServletResponse resp, Object arg2, ModelAndView arg3) {

	}

	@Override
	public boolean before(HttpServletRequest req, HttpServletResponse resp, Object arg2) {
		String cookieToken = ControllerUtil.getCookieValue(TOKEN);
		String sessionToken = (String) req.getSession().getAttribute(TOKEN);
		String redirectUrl = (String) req.getSession().getAttribute(REDIRECT_URL);
		ControllerUtil.removeCookie(resp, TOKEN);
		req.getSession().removeAttribute(TOKEN);
		if (!StringUtil.equals(cookieToken, sessionToken)) {
			try {
				LogWriter.warn("the request is suspected a Csrf attack");
				resp.sendRedirect(StringUtil.isNotEmpty(redirectUrl) ? redirectUrl : BaseInfo.getContextPath());
			} catch (Exception e) {
				LogWriter.error("Csrf redirect error");
			}
			return false;
		}
		return true;
	}

	@Override
	public void complete(HttpServletRequest req, HttpServletResponse resp, Object arg2, Exception arg3) {
	}
}

下面是我自己重新改造的util,你们可以直接使用

public class ControllerUtil {
	public static void addCookie(HttpServletResponse var0, String var1, String var2, int var3) {
        addCookie(var0, var1, var2, var3, (String)null);
    }
    public static void addCookie(HttpServletResponse var0, String var1, String var2, int var3, String var4) {
        addCookie(var0, var1, var2, var3, var4, false);
    }
    public static void addCookie(HttpServletResponse var0, String var1, String var2, int var3, String var4, boolean var5) {
        Cookie var6 = new Cookie(var1, StringUtil.encoder(var2));
        var6.setMaxAge(var3);
        if (StringUtil.isNotEmpty(var4)) {
            var6.setPath(var4);
        }

        a(var0, var6, var5);
    }
    private static void a(HttpServletResponse var0, Cookie var1, boolean var2) {
        String var3 = var1.getName();
        String var4 = var1.getValue();
        int var5 = var1.getMaxAge();
        String var6 = var1.getPath();
        String var7 = var1.getDomain();
        boolean var8 = var1.getSecure();
        StringBuilder var9 = new StringBuilder();
        var9.append(var3).append("=").append(var4).append(";");
        if (var5 == 0) {
            var9.append("Expires=Thu Jan 01 08:00:00 CST 1970;");
        } else if (var5 > 0) {
            var9.append("Max-Age=").append(var5).append(";");
        }

        if (var7 != null) {
            var9.append("domain=").append(var7).append(";");
        }

        if (var6 != null) {
            var9.append("path=").append(var6).append(";");
        }

        if (var8) {
            var9.append("secure;");
        }

        if (var2) {
            var9.append("HTTPOnly;");
        }

        var0.addHeader("Set-Cookie", var9.toString());
    }
}

到此基本就已经没什么大问题了,其实最好的方式就是平时编码时要注意接口参数,对其参数进行判断,这样能很好地预防各种攻击

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值