什么是csrf?

csrf又称跨域请求伪造,攻击方通过伪造用户请求访问受信任站点。CSRF这种攻击方式在2000年已经被国外的安全人员提出,但在国内,直到06年才开始被关注,08年,国内外的多个大型社区和交互网站分别爆出CSRF漏洞,如:NYTimes.com(纽约时报)、Metafilter(一个大型的BLOG网站),YouTube和百度HI......而现在,互联网上的许多站点仍对此毫无防备,以至于安全业界称CSRF为“沉睡的巨人”。
举个例子,用户通过表单发送请求到银行网站,银行网站获取请求参数后对用户账户做出更改。在用户没有退出银行网站情况下,访问了攻击网站,攻击网站中有一段跨域访问的代码,可能自动触发也可能点击提交按钮,访问的url正是银行网站接受表单的url。因为都来自于用户的浏览器端,银行将请求看作是用户发起的,所以对请求进行了处理,造成的结果就是用户的银行账户被攻击网站修改。
解决方法基本上都是增加攻击网站无法获取到的一些表单信息,比如增加图片验证码,可以杜绝csrf攻击,但是除了登陆注册之外,其他的地方都不适合放验证码,因为降低了网站易用性
相关介绍:
http://baike.baidu.com/view/1609487.htm?fr=aladdin

spring-servlet中配置csrf

 <!-- Spring csrf 拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/login" />
<bean class="com.wangzhixuan.commons.csrf.CsrfInterceptor" />
</mvc:interceptor>
</mvc:interceptors>

在类中声明Csrf拦截器,用来生成或去除CsrfToken

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import com.wangzhixuan.commons.scan.ExceptionResolver;
import com.wangzhixuan.commons.utils.WebUtils; /**
* Csrf拦截器,用来生成或去除CsrfToken
*
* @author L.cm
*/
public class CsrfInterceptor extends HandlerInterceptorAdapter {
private static final Logger logger = LogManager.getLogger(ExceptionResolver.class); @Autowired
private CsrfTokenRepository csrfTokenRepository; @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 非控制器请求直接跳出
if (!(handler instanceof HandlerMethod)) {
return true;
}
CsrfToken csrfToken = handlerMethod.getMethodAnnotation(CsrfToken.class);
// 判断是否含有@CsrfToken注解
if (null == csrfToken) {
return true;
}
// create、remove同时为true时异常
if (csrfToken.create() && csrfToken.remove()) {
logger.error("CsrfToken attr create and remove can Not at the same time to true!");
return renderError(request, response, Boolean.FALSE, "CsrfToken attr create and remove can Not at the same time to true!");
}
// 创建
if (csrfToken.create()) {
CsrfTokenBean token = csrfTokenRepository.generateToken(request);
csrfTokenRepository.saveToken(token, request, response);
// 缓存一个表单页面地址的url
csrfTokenRepository.cacheUrl(request, response);
request.setAttribute(token.getParameterName(), token);
return true;
}
// 判断是否ajax请求
boolean isAjax = WebUtils.isAjax(handlerMethod);
// 校验,并且清除
CsrfTokenBean tokenBean = csrfTokenRepository.loadToken(request);
if (tokenBean == null) {
return renderError(request, response, isAjax, "CsrfToken is null!");
}
String actualToken = request.getHeader(tokenBean.getHeaderName());
if (actualToken == null) {
actualToken = request.getParameter(tokenBean.getParameterName());
}
if (!tokenBean.getToken().equals(actualToken)) {
return renderError(request, response, isAjax, "CsrfToken not eq!");
}
return true;
} private boolean renderError(HttpServletRequest request, HttpServletResponse response,
boolean isAjax, String message) throws IOException {
// 获取缓存的cacheUrl
String cachedUrl = csrfTokenRepository.getRemoveCacheUrl(request, response);
// ajax请求直接抛出异常,因为{@link ExceptionResolver}会去处理
if (isAjax) {
throw new RuntimeException(message);
}
// 非ajax CsrfToken校验异常,先清理token
csrfTokenRepository.saveToken(null, request, response);
logger.info("Csrf[redirectUrl]:\t" + cachedUrl);
response.sendRedirect(cachedUrl);
return false;
} /**
* 用于清理@CsrfToken保证只能请求成功一次
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 非控制器请求直接跳出
if (!(handler instanceof HandlerMethod)) {
return;
}
CsrfToken csrfToken = handlerMethod.getMethodAnnotation(CsrfToken.class);
if (csrfToken == null || !csrfToken.remove()) {
return;
}
csrfTokenRepository.getRemoveCacheUrl(request, response);
csrfTokenRepository.saveToken(null, request, response);
} }

声明Csrf过滤注解,通过标注来过滤对应的请求

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* Csrf过滤注解
* @author L.cm
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CsrfToken {
boolean create() default false;
boolean remove() default false;
}

建立实例对象(操作对象)

import java.io.Serializable;

import org.springframework.util.Assert;

public class CsrfTokenBean implements Serializable {
private static final long serialVersionUID = -6865031901744243607L; private final String token;
private final String parameterName;
private final String headerName; /**
* Creates a new instance
* @param headerName the HTTP header name to use
* @param parameterName the HTTP parameter name to use
* @param token the value of the token (i.e. expected value of the HTTP parameter of
* parametername).
*/
public CsrfTokenBean(String headerName, String parameterName, String token) {
Assert.hasLength(headerName, "headerName cannot be null or empty");
Assert.hasLength(parameterName, "parameterName cannot be null or empty");
Assert.hasLength(token, "token cannot be null or empty");
this.headerName = headerName;
this.parameterName = parameterName;
this.token = token;
} public String getHeaderName() {
return this.headerName;
} public String getParameterName() {
return this.parameterName;
} public String getToken() {
return this.token;
} }

过滤过程中需要的仓库

package com.wangzhixuan.commons.csrf;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public interface CsrfTokenRepository {
/**
* Generates a {@link CsrfTokenBean}
*
* @param request the {@link HttpServletRequest} to use
* @return the {@link CsrfTokenBean} that was generated. Cannot be null.
*/
CsrfTokenBean generateToken(HttpServletRequest request); /**
* Saves the {@link CsrfTokenBean} using the {@link HttpServletRequest} and
* {@link HttpServletResponse}. If the {@link CsrfTokenBean} is null, it is the same as
* deleting it.
*
* @param token the {@link CsrfTokenBean} to save or null to delete
* @param request the {@link HttpServletRequest} to use
* @param response the {@link HttpServletResponse} to use
*/
void saveToken(CsrfTokenBean token, HttpServletRequest request,
HttpServletResponse response); /**
* Loads the expected {@link CsrfTokenBean} from the {@link HttpServletRequest}
*
* @param request the {@link HttpServletRequest} to use
* @return the {@link CsrfTokenBean} or null if none exists
*/
CsrfTokenBean loadToken(HttpServletRequest request); /**
* 缓存来源的url
* @param request request the {@link HttpServletRequest} to use
* @param response the {@link HttpServletResponse} to use
*/
void cacheUrl(HttpServletRequest request, HttpServletResponse response); /**
* 获取并清理来源的url
* @param request the {@link HttpServletRequest} to use
* @param response the {@link HttpServletResponse} to use
* @return 来源url
*/
String getRemoveCacheUrl(HttpServletRequest request, HttpServletResponse response); }

HttpSessionCsrfTokenRepository

package com.wangzhixuan.commons.csrf;

import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import com.wangzhixuan.commons.utils.StringUtils; public final class HttpSessionCsrfTokenRepository implements CsrfTokenRepository {
private static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf";
private static final String DEFAULT_CSRF_HEADER_NAME = "X-CSRF-TOKEN";
private static final String DEFAULT_CSRF_TOKEN_ATTR_NAME = HttpSessionCsrfTokenRepository.class
.getName().concat(".CSRF_TOKEN");
private static final String DEFAULT_CACHE_URL_ATTR_NAME = HttpSessionCsrfTokenRepository.class
.getName().concat(".CACHE_URL"); private String parameterName = DEFAULT_CSRF_PARAMETER_NAME;
private String headerName = DEFAULT_CSRF_HEADER_NAME;
private String sessionAttributeName = DEFAULT_CSRF_TOKEN_ATTR_NAME;
private String cacheUrlAttributeName = DEFAULT_CACHE_URL_ATTR_NAME; /*
* (non-Javadoc)
*
* @see org.springframework.security.web.csrf.CsrfTokenRepository#saveToken(org.
* springframework .security.web.csrf.CsrfToken,
* javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
public void saveToken(CsrfTokenBean token, HttpServletRequest request,
HttpServletResponse response) {
if (token == null) {
HttpSession session = request.getSession(false);
if (session != null) {
session.removeAttribute(this.sessionAttributeName);
}
}
else {
HttpSession session = request.getSession();
session.setAttribute(this.sessionAttributeName, token);
}
} /*
* (non-Javadoc)
*
* @see
* org.springframework.security.web.csrf.CsrfTokenRepository#loadToken(javax.servlet
* .http.HttpServletRequest)
*/
public CsrfTokenBean loadToken(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) {
return null;
}
return (CsrfTokenBean) session.getAttribute(this.sessionAttributeName);
} /*
* (non-Javadoc)
*
* @see org.springframework.security.web.csrf.CsrfTokenRepository#generateToken(javax.
* servlet .http.HttpServletRequest)
*/
public CsrfTokenBean generateToken(HttpServletRequest request) {
return new CsrfTokenBean(this.headerName, this.parameterName,
createNewToken());
} private String createNewToken() {
return UUID.randomUUID().toString();
} @Override
public void cacheUrl(HttpServletRequest request, HttpServletResponse response) {
String queryString = request.getQueryString();
// 被拦截前的请求URL
String redirectUrl = request.getRequestURI();
if (StringUtils.isNotBlank(queryString)) {
redirectUrl = redirectUrl.concat("?").concat(queryString);
}
HttpSession session = request.getSession();
session.setAttribute(this.cacheUrlAttributeName, redirectUrl);
} @Override
public String getRemoveCacheUrl(HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession(false);
if (session == null) {
return null;
}
String redirectUrl = (String) session.getAttribute(this.cacheUrlAttributeName);
if (StringUtils.isBlank(redirectUrl)) {
return null;
}
session.removeAttribute(this.cacheUrlAttributeName);
return redirectUrl;
} }
 

spring-security中的csrf防御机制(跨域请求伪造)的更多相关文章

  1. spring boot 中 2.X 的跨域请求

    解决跨域: @Configuration @EnableAutoConfiguration public class ZooConfiguration { @Bean public FilterReg ...

  2. Spring Boot Web应用开发 CORS 跨域请求支持:

    Spring Boot Web应用开发 CORS 跨域请求支持: 一.Web开发经常会遇到跨域问题,解决方案有:jsonp,iframe,CORS等等CORS与JSONP相比 1. JSONP只能实现 ...

  3. js中ajax如何解决跨域请求

    js中ajax如何解决跨域请求,在讲这个问题之前先解释几个名词 1.跨域请求 所有的浏览器都是同源策略,这个策略能保证页面脚本资源和cookie安全 ,浏览器隔离了来自不同源的请求,防上跨域不安全的操 ...

  4. Django框架 之 跨域请求伪造

    Django框架 之 跨域请求伪造 浏览目录 同源策略与Jsonp 同源策略 Jsonp jQuery对JSONP的实现 CORS 简介 两种请求 同源策略与Jsonp 同源策略 同源策略(Same ...

  5. CSRF/XSRF 跨站请求伪造

    CSRF/XSRF 跨站请求伪造 CSRF(Cross Site Request Forgery, 跨站域请求伪造)也称 XSRF, 是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安 ...

  6. web 安全 & web 攻防: XSS(跨站脚本攻击)和 CSRF(跨站请求伪造)

    web 安全 & web 攻防: XSS(跨站脚本攻击)和 CSRF(跨站请求伪造) XSS(跨站脚本攻击)和CSRF(跨站请求伪造) Cross-site Scripting (XSS) h ...

  7. ValidateAntiForgeryToken 防止CSRF(跨网站请求伪造)

    用途:防止CSRF(跨网站请求伪造). 用法:在View->Form表单中:<%:Html.AntiForgeryToken()%> 在Controller->Action动作 ...

  8. revel框架教程之CSRF(跨站请求伪造)保护

    revel框架教程之CSRF(跨站请求伪造)保护 CSRF是什么?请看这篇博文“浅谈CSRF攻击方式”,说的非常清楚. 现在做网站敢不防CSRF的我猜只有两种情况,一是没什么人访问,二是局域网应用.山 ...

  9. CSRF(跨站请求伪造)攻击

    CSRF(跨站请求伪造)攻击 CSRF(Cross Site Request Forgery,跨站请求伪造)是一种近年来才逐渐被大众了解的网络攻击方式,又被称为One-Click Attack或Ses ...

随机推荐

  1. Tomcat 容器的安全认证和鉴权

    大量的 Web 应用都有安全相关的需求,正因如此,Servlet 规范建议容器要有满足这些需求的机制和基础设施,所以容器要对以下安全特性予以支持: 身份验证:验证授权用户的用户名和密码 资源访问控制: ...

  2. HDU5997 【线段树】

    思路: 用vector存一下各种颜色的区间,每次处理颜色的区间,相同颜色不需要更新.区间最多1e6个没错,但是随着颜色的更替区间只会越来越少. 维护区间左右两端的颜色,lazy一下. 区间合并的时候 ...

  3. [Xcode 实际操作]一、博主领进门-(15)读取当前应用的信息

    目录:[Swift]Xcode实际操作 本文将演示读取当前应用的配置信息. 在项目导航区,打开视图控制器的代码文件[ViewController.swift] import UIKit class V ...

  4. suse 11入门学习

    1 去掉显示隐藏文件的默认配置 vi /etc/bash.bashrc 找到 ; then LS_OPTIONS="-A -N $LS_OPTIONS -T 0" else LS_ ...

  5. E - Multiplication Puzzle

    #include <iostream> #include <algorithm> #include <cstring> #include <cstdio> ...

  6. 子div块中设置margin-top时影响父div块位置的解决办法及其原因

    解决办法①: 若子DIV块中使用margin-top,则在父DIV块中添加:overflow:hidden; 解决办法②: 在子DIV块中用padding-top代替margin-top. 有个叫 b ...

  7. socketserver 入门练习

    个人理解: 个人感觉socketserver其实就是为服务端专门提供的一个用于解决多用户并发访问需求的一个模块 小试牛刀: 服务端socketserver_server.py import socke ...

  8. 洛谷P2514||bzoj2426 [HAOI2010]工厂选址

    洛谷P2514 bzoj2426 其实是个简单的贪心,然而不适合在脑子不清醒的时候做...看不懂题意续了1个小时 很容易发现应该枚举新建哪个发电厂,对于这种方案就是取其中b吨煤运到原来发电厂,取剩下( ...

  9. morphia(6-1)-查询

    1.filter morphia语法: query.filter("price >=", 1000); mongodb语法: { price: { $gte: 1000 } ...

  10. 最耗资源的10条sql

    ----当前最耗资源的10个cpu select * from (select address,hash_value, round(cpu_time/1000000) cpu_time_s, roun ...