(一)简要介绍
跨站请求伪造(Cross-site request fogery),也被称为one-click attack或者session riding,通常缩写为CSRF或者XSRF,是一种挟制用户在当前已登录的WEB应用程序上执行非本意的操作的攻击方法。
举个栗子:
- 用户小z登录了网站A,同时打开网站B
- 网站B隐蔽的发送一个请求至网站A
- 网站A通过session、cookie等身份标记判断是用户小z,执行对应操作
这样网站B内的非法代码就盗用了用户小z的身份,在小z不知情的情况下执行了攻击者需要的操作,这就是跨站请求伪造。
(二)术语解释:
CSRF Token
服务端为客户端生成令牌,这个令牌将用于请求合法性校验,一般通过请求头或请求参数传递到服务端。
CSRF Token仓库
服务端组件,用于从请求加载或生成CSRF Token。Spring Security提供了Cookie和HttpSession两种实现。
CSRF请求校验匹配器
服务端组件,用于判断请求是否需要CSRF校验。
(三)防止攻击逻辑
- 利用CSRF Token仓库将Http请求获取CSRF Token(该过程可以理解为Web服务端针对当前请求获取CSRF Token)。
- 通过CSRF Token校验请求匹配器来判断当前请求是否需要CSRF Token校验,若需要,执行下一步;否则,跳过校验。
- 先从请求头中获取CSRF Token值,若不存在,再从请求参数中获取。(该过程可以理解为获取Web客户端请求中的CSRF Token):
若均未获取到,将会转向错误页面,并且相应头状态码为:403;
若CSRF Token值获取到,执行下一步。 - 将第1步CSRF Token仓库获取的CSRF Token与客户端请求中的CSRF Token进行比较:
若两值相同,校验通过;
若不相同,校验失败,将会转向错误页面,并且相应状态码为:403。
(四)CSRF Token仓库
接口:org.springframework.security.web.csrf.CsrfTokenRepository
Cookie类型(默认)
实现类:org.springframework.security.web.csrf.CookieCsrfTokenRepository
CSRF Token存储:客户端,Web浏览器Cookie
有效时间:Web浏览器会话期间
特别注意:Cookie方式安全系数相对较低
HttpSession类型
实现类:org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository
CSRF Token存储:服务端,HttpSession(Servlet容器)
有效时间:HttpSession 最大不活动时间间隔(#setMaxInactiveInterval(int) )
特别注意:Servlet 容器需要支持HttpSession复制(分布式HttpSession)
(五)案例演示
添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
1、创建login.html页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<form id="form" method="post">
<label>用户名:</label><input name="username" type="text" value="" />
<label>密码:</label><input name="password" type="text" value="" />
<!--csrf验证需要-->
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<br/>
<input type="submit" value="登录">
</form>
</body>
</html>
2、创建Web 安全配置类
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().csrfTokenRepository(new CookieCsrfTokenRepository())
.requireCsrfProtectionMatcher(
/**
* 拦截“/login”开头的访问路径,不让访问
* 拦截所有“POST”请求,不让访问
*/
// httpServletRequest -> httpServletRequest.getRequestURI().startsWith("/login")
// && httpServletRequest.getMethod().equals("POST")
httpServletRequest -> httpServletRequest.getMethod().equals("POST")
);
}
}
3、创建Controller
@Controller
public class SecurityController {
@GetMapping("/login")
public String login(){
return "login";
}
@GetMapping("/index")
public String index(){
return "index";
}
}
4、案例演示效果
在我们提交表单的时候,由于请求方法时"POST",所以该请求会被拦截住!