2-SpringSecurity:CSRF攻击

简介: 2-SpringSecurity:CSRF攻击

背景


本系列教程,是作为团队内部的培训资料准备的。主要以实验的方式来体验SpringSecurity的各项Feature。


实验0:SpringSecurity默认开启CSRF防护


现在我们在springboot-security项目的HelloController.java中新增一个POST接口:/ok

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello(){
        return "hello springsecurity";
    }
    @PostMapping("/ok")
    public String ok(){
        return "ok";
    }
}

当然这个POST接口无法直接在浏览器中发起请求,我们需要借助PostMan来实现POST请求的发送。把浏览器中的Cookie复制到PostMan中。


  • 先发GET /hello,正常

image.png

  • 再发POST /ok,403了。。

image.png

那么,问题来了,两个请求都是在登录状态下进行的,为什么GET成功,POST返回403了?


其实SpringSecurity默认就开启了CSRF防护,这在上一篇及官网中关于SpringBoot自动配置项那里可以看到。并且SpringSecurity默认忽略"GET", "HEAD", "TRACE", "OPTIONS"等请求,源码如下:

/**
 * Specify the {@link RequestMatcher} to use for determining when CSRF should be
 * applied. The default is to ignore GET, HEAD, TRACE, OPTIONS and process all other
 * requests.
 *
 * @param requireCsrfProtectionMatcher the {@link RequestMatcher} to use
 * @return the {@link CsrfConfigurer} for further customizations
 */
public CsrfConfigurer<H> requireCsrfProtectionMatcher(
        RequestMatcher requireCsrfProtectionMatcher) {
    Assert.notNull(requireCsrfProtectionMatcher,
            "requireCsrfProtectionMatcher cannot be null");
    this.requireCsrfProtectionMatcher = requireCsrfProtectionMatcher;
    return this;
}
private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
    private final HashSet<String> allowedMethods = new HashSet<>(
            Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));
    /*
     * (non-Javadoc)
     *
     * @see
     * org.springframework.security.web.util.matcher.RequestMatcher#matches(javax.
     * servlet.http.HttpServletRequest)
     */
    @Override
    public boolean matches(HttpServletRequest request) {
        return !this.allowedMethods.contains(request.getMethod());
    }
}


实验1:CSRF GET攻击


CSRF: Cross-Site Request Forgery 跨站请求伪造。一些网站比如知乎、简书中的外部链接,点击之后会有提示(免责声明),此操作有风险是否继续,这便与CSRF密切相关。

image.png

结合上篇文章,新建一个SpringBoot项目,起名spring-security-csrf,核心依赖为WebThymeleaf,模拟一个钓鱼网站。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

建好项目后,创建一个简单的HelloController.java,包含一个/GET请求,返回一个页面index.html:


  • 后端接口
@Controller
public class HelloController {
    @RequestMapping("/")
    public String hello(){
        return "index";
    }
}
  • 前端模板
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
Fishing: <img src="http://hello:8080/hello">
</body>
</html>

Note:


  • 以下步骤在Firefox浏览器完成;


  • 修改springboot-security的后端接口,增加输出打印,方便后续确定请求是否进入后端;
@RestController
@Slf4j
public class HelloController {
    @GetMapping("/hello")
    public String hello(){
        log.info("Hello ");
        return "hello";
    }
    @PostMapping("/ok")
    public String ok(){
        log.info("ok");
        return "ok";
    }
}
  • 前提:为了模拟不同域名下的请求(即CSRF),我们在本地的hosts文件添加如下内容:
127.0.0.1 hello
127.0.0.1 world

实验步骤:


  1. 启动两个项目:springboot-security在8080端口、spring-security-csrf在8081端口;


  1. 打开浏览器,访问http://hello:8080,并完成登录,访问http://hello:8080/hello接口,观察项目springboot-security后台打印输出;


  1. 在同一个浏览器,访问http://world:8081,默认进入index.html,同时观察项目springboot-security后台打印输出;


实验结果:


  1. 在同一个浏览器(此处为Firefox,Chrome内核的浏览器未成功)的不同Tab下,在world域名下请求hello域名下的GET接口/hello:<img src="http://hello:8080/hello">,请求成功到达源站后端,实现了CSRF:跨站请求伪造。


  1. 验证了SpringSecurity虽然默认开启CSRF防护,但是幂等请求诸如"GET", "HEAD", "TRACE", "OPTIONS"被忽略。


实验2:CSRF POST攻击


spring-security-csrf项目中模拟一个按钮操作,发起POST请求,这里采用原生JavaScript发起AjaxPOST请求:http://hello:8080/ok

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
Fishing: <img src="http://hello:8080/hello">
<script language="JavaScript">
    function click() {
        let xhr = new XMLHttpRequest();
        xhr.open("POST", "http://hello:8080/ok", true);
        xhr.onload = function (e) {
            console.log("response: ", e.target);
        }
        xhr.onerror = function (e) {
            console.log("error: ", e)
        }
        xhr.send(null);
    }
    click();
</script>
</body>
</html>

实验步骤:


  1. 启动两个项目:springboot-security在8080端口、spring-security-csrf在8081端口;


  1. 打开浏览器,访问http://hello:8080,并完成登录,PostMan访问http://hello:8080/ok接口,观察项目springboot-security后台打印输出;


  1. 在同一个浏览器,访问http://world:8081,默认进入index.html,同时观察项目springboot-security后台打印输出;


image.png

实验结果:


  1. 在同一个浏览器(此处为Firefox,Chrome内核的浏览器未成功)的不同Tab下,在world域名下请求hello域名下的POST接口/ok:http://hello:8080/ok",Ajax请求受浏览器同源策略限制,被拦截。CSRF攻击失败。


  1. 即使通过PostMan访问:http://hello:8080/ok",附带Cookie,结果也是被拦截,返回403;


  1. 验证了SpringSecurity默认开启CSRF防护,对于非幂等请求诸如"POST", "PUT", "DELETE"等请求进行拦截。


因此,相比GET请求,POST请求相对更安全。


开启了CSRF防护之后,那么问题来了:


  1. 我们后端的POST请求都被拦截了,前端难道就没办法发起POST请求了吗?


  1. 我们何时需要CSRF防护,何时开启?


这些问题,后续将一一解答。


相关实践学习
基于函数计算快速搭建Hexo博客系统
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Hexo博客。
目录
相关文章
|
2月前
|
JavaScript 安全 前端开发
js开发:请解释什么是XSS攻击和CSRF攻击,并说明如何防范这些攻击。
XSS和CSRF是两种常见的Web安全威胁。XSS攻击通过注入恶意脚本盗取用户信息或控制账户,防范措施包括输入验证、内容编码、HTTPOnly Cookie和CSP。CSRF攻击则诱使用户执行未经授权操作,防范手段有CSRF Tokens、双重验证、Referer检查和SameSite Cookie属性。开发者应采取这些防御措施并定期进行安全审计以增强应用安全性。
24 0
|
6月前
|
安全 NoSQL Java
互联网并发与安全系列教程(06) - 常见的Web安全漏洞(CSRF攻击)
互联网并发与安全系列教程(06) - 常见的Web安全漏洞(CSRF攻击)
72 0
|
7月前
|
SQL 安全 前端开发
渗透攻击实例-邪恶的CSRF(社会工程学)
渗透攻击实例-邪恶的CSRF(社会工程学)
|
19天前
|
缓存 安全 JavaScript
前端安全:Vue应用中防范XSS和CSRF攻击
【4月更文挑战第23天】本文探讨了在Vue应用中防范XSS和CSRF攻击的重要性。XSS攻击通过注入恶意脚本威胁用户数据,而CSRF则利用用户身份发起非授权请求。防范措施包括:对输入内容转义、使用CSP、选择安全的库;采用Anti-CSRF令牌、同源策略和POST请求对抗CSRF;并实施代码审查、更新依赖及教育团队成员。通过这些实践,可提升Vue应用的安全性,抵御潜在攻击。
|
13天前
|
存储 JavaScript 前端开发
Django的CSRF防攻击原理详解
Django的CSRF防攻击原理详解
|
17天前
|
安全 前端开发 JavaScript
在Python Web开发过程中:Web框架相关,如何在Web应用中防止CSRF攻击?
在Python Web开发中防范CSRF攻击的关键措施包括:验证HTTP Referer字段、使用CSRF token、自定义HTTP头验证、利用Web框架的防护机制(如Django的`{% csrf_token %}`)、Ajax请求时添加token、设置安全会话cookie及教育用户提高安全意识。定期进行安全审计和测试以应对新威胁。组合运用这些方法能有效提升应用安全性。
22 0
|
8月前
|
安全 PHP 开发者
CSRF 攻击的防范措施
CSRF 攻击的防范措施
|
4月前
|
安全 前端开发 Java
什么是 CSRF 攻击?
什么是 CSRF 攻击?
|
4月前
|
安全 JavaScript 前端开发
Python 的安全性和测试:解释什么是 XSS 和 CSRF 攻击?在 Python 中如何防范这些攻击?
Python 的安全性和测试:解释什么是 XSS 和 CSRF 攻击?在 Python 中如何防范这些攻击?
|
8月前
|
安全
浏览器基础原理-安全: CSRF攻击
浏览器基础原理-安全: CSRF攻击
54 0