CSRF防护

本文介绍了CSRF(跨站请求伪造)的基本概念及其工作原理,并详细阐述了如何在Django框架中实现CSRF防护机制,包括在表单中使用token及通过AJAX请求添加X-CSRFToken头。

CSRF

1、什么是CSRF?

  CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

2、原理
CSRF防护

从上图可以看出,要完成一次CSRF***,受害者必须依次完成两个步骤 :
•1.登录受信任网站A,并在本地生成Cookie 。
•2.在不退出A的情况下,访问危险网站B。

ps:注意并不是你退出了A后登陆B就会没事,因为你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。

CSRF预防机制

CSRF的防御可以从服务端和客户端两方面着手,防御效果是从服务端着手效果比较好,现在一般的CSRF防御也都在服务端进行。

token防御的整体思路是:

第一步:后端随机产生一个token,把这个token保存在SESSION状态中;同时,后端把这个token交给前端页面;


第二步:下次前端需要发起请求(比如发帖)的时候把这个token加入到请求数据或者头信息中,一起传给后端;


第三步:后端校验前端请求带过来的token和SESSION里的token是否一致;

1、Django下的CSRF预防机制

django 第一次响应来自某个客户端的请求时,会在服务器端随机生成一个 token,把这个 token 放在 cookie 里。然后每次 POST 请求都会带上这个 token,

这样就能避免被 CSRF ***。

在 templete 中, 为每个 POST form 增加一个 {% csrf_token %} tag. 如下:
1.在返回的 HTTP 响应的 cookie 里,django 会为你添加一个 csrftoken 字段,其值为一个自动生成的 token
2.在所有的 POST 表单模板中,加一个{% csrf_token %} 标签,它的功能其实是给form增加一个隐藏的input标签,如下
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">,而这个csrf_token = cookie.csrftoken,在渲染模板时context中有context['csrf_token'] = request.COOKIES['csrftoken']

3.
在通过表单发送POST到服务器时,表单中包含了上面隐藏了crsrmiddlewaretoken这个input项,服务端收到后,django 会验证这个请求的 cookie 里的 csrftoken 字段的值和提交的表单里的 csrfmiddlewaretoken 字段的值是否一样。如果一样,则表明这是一个合法的请求,否则,这个请求可能是来自于别人的 csrf ***,返回 403 Forbidden.

4.在通过 ajax 发送POST请求到服务器时,要求增加一个x-csrftoken header,其值为 cookie 里的 csrftoken 的值,服务湍收到后,django会验证这个请求的cookie里的csrftoken字段与ajax post消息头中的x-csrftoken header是否相同,如果相同,则表明是一个合法的请求

具体实现方法

django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。而对于django中设置防跨站请求伪造功能有分为全局和局部。

全局:

  中间件 django.middleware.csrf.CsrfViewMiddleware

局部:br/>•@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
•@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

注:from django.views.decorators.csrf import csrf_exempt,csrf_protect

1、原理

  在客户端页面上添加csrftoken, 服务器端进行验证,服务器端验证的工作通过'django.middleware.csrf.CsrfViewMiddleware'这个中间层来完成。在django当中防御csrf***的方式有两种:

   1.在表单当中附加csrftoken

   2.通过request请求中添加X-CSRFToken请求头。

注意:Django默认对所有的POST请求都进行csrftoken验证,若验证失败则403错误侍候。

2、应用

1、在表单中附加csrftoken

> veiw中设置返回值:
>   return render_to_response('Account/Login.html',data,context_instance=RequestContext(request)) 
> #在渲染模块时,使用 RequestContext。RequestContext 会处理 csrf_token 这个 tag,  从而自动为表单添加一个名为 csrfmiddlewaretoken 的 input 
>      或者
>      return render(request, 'xxx.html', data)   #使用render则会自动生成,不用ReqestContext
>   
> html中设置Token:
> {% csrf_token %}

2、Ajax

对于传统的form,可以通过表单的方式将token再次发送到服务端,而对于ajax的话,使用如下方式。

> from django.template.context import RequestContext
> # Create your views here.
>   
>   
> def test(request):
>   
>     if request.method == 'POST':
>         print request.POST
>         return HttpResponse('ok')
>     return  render_to_response('app01/test.html',context_instance=RequestContext(request))
>       
    前端:

  2、在进行post提交时,获取Cookie当中的csrftoken并在请求中添加X-CSRFToken请求头, 该请求头的数据就是csrftoken。通过$.ajaxSetup方法设置AJAX请求的默认参数选项, 在每次ajax的POST请求时,添加X-CSRFToken请求头

> ```

> <!DOCTYPE html>
> <html>
> <head lang="en">
>     <meta charset="UTF-8">
>     <title></title>
> </head>
> <body>
>     {% csrf_token %}
>   
>     <input type="button" onclick="Do();"  value="Do it"/>
>   
>     <script src="/static/plugin/jquery/jquery-1.8.0.js"></script>
>     <script src="/static/plugin/jquery/jquery.cookie.js"></script>
>     <script type="text/javascript">
>         var csrftoken = $.cookie('csrftoken');
>   
>         function csrfSafeMethod(method) {
>             // these HTTP methods do not require CSRF protection
>             return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
>         }
>         $.ajaxSetup({
>             beforeSend: function(xhr, settings) {
>                 if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
>                     xhr.setRequestHeader("X-CSRFToken", csrftoken);
>                 }
>             }
>         });
>         function Do(){
>   
>             $.ajax({
>                 url:"/app01/test/",
>                 data:{id:1},
>                 type:'POST',
>                 success:function(data){
>                     console.log(data);
>                 }
>             });
>   
>         }
>     </script>
> </body>
> </html>


摘自:https://www.cnblogs.com/freely/p/6928822.html

转载于:https://blog.51cto.com/wangheyu1/2115647

<think>嗯,用户现在想了解Spring Security中的CSRF防护原理和实现方法。之前我已经给过他们一个关于Spring Security的入门教程,现在需要深入讲解CSRF部分。首先,我需要回顾之前的对话,确保内容连贯。用户提到了站内的引用内容,这些引用涉及到CSRF的默认开启、如何处理POST请求以及异步请求的问题。 用户的问题明确,需要解释CSRF的概念、原理以及在Spring Security中的具体实现。我需要先定义CSRF攻击是什么,可能用户对基础概念还不清楚,所以需要简明扼要地解释。例如,攻击者如何利用用户的已认证状态发起恶意请求,比如转账操作。 接下来是原理部分。根据引用内容,Spring Security默认启用CSRF防护,尤其是对PATCH、POST、PUT和DELETE方法。这里需要说明为什么这些方法会被保护,而GET等幂等方法被忽略。需要提到同步器令牌模式,也就是生成随机token,并在请求中验证。引用的内容里提到token存储在session或cookie中,每次请求需要携带,服务器进行比对。 然后是具体实现方法。根据引用,用户在表单中需要添加_csrf的token,后端配置默认开启。需要分步骤说明如何配置,包括表单提交、前后端分离的情况,以及异步请求的处理。比如,在Thymeleaf中自动添加token,或者手动添加。对于异步请求,如AJAX,需要从meta标签获取token并添加到请求头中,引用中提到的例子需要详细说明。 可能用户会遇到的常见问题,比如为什么登录POST请求被CSRF防护拦截,以及如何排除特定路径。需要解释登录请求虽然是POST,但Spring Security默认忽略,但如果配置了自定义登录页,可能需要处理。同时,如何关闭CSRF防护,尽管不推荐,但有时候在测试或某些API场景可能需要。 需要结合用户提供的引用内容,比如引用2中提到登录必须携带_csrf参数,否则403错误。这时候可能需要指导用户在表单中添加隐藏字段,或者如何在请求头中包含token。同时,引用3提到异步请求的处理,比如在meta标签中存储token,并在AJAX请求中携带,这部分需要详细步骤。 最后,常见问题部分需要覆盖用户可能遇到的困惑,比如配置后仍然出现403,可能的原因是什么,如何调试。比如检查token是否正确携带,配置是否正确,或者路径是否被正确排除。 在组织回答时,要保持结构清晰,分点说明,使用代码示例和配置示例,帮助用户一步步理解和实现。同时,引用提供的资料作为参考,确保信息准确。需要确保语言口语化,避免过于技术化的术语,但也要准确表达概念。可能还需要提醒用户,尽管CSRF默认开启,但在某些情况下需要自定义配置,比如API网关或微服务架构中的特殊处理。 总结一下,回答需要包括CSRF的定义、攻击原理,Spring Security的防护机制(同步器令牌),具体实现步骤(表单、RESTful API、异步请求),配置方法,以及常见问题处理。这样用户能够全面了解并应用到实际项目中。</think>#### Spring Security 中 CSRF 防护的深度解析 --- ### 一、CSRF 攻击原理 **跨站请求伪造(CSRF)** 是一种利用用户已认证的身份,通过伪造请求执行非授权操作的攻击方式。例如: - 用户登录银行网站后,访问恶意网站,后者诱导浏览器自动发送转账请求(携带用户的 Cookie)[^3]。 - 攻击者构造隐藏表单,利用用户会话劫持敏感操作(如修改密码)。 --- ### 二、Spring Security 的防护机制 #### 1. **同步器令牌模式(Synchronizer Token Pattern)** - **核心原理**:服务器生成随机 Token(如 `_csrf`),存储于 Session 或 Cookie,要求客户端在提交非幂等请求(POST/PUT/PATCH/DELETE)时携带该 Token 进行验证[^1][^2]。 - **实现流程**: 1. 用户首次访问页面时,服务端生成唯一 Token。 2. 前端通过表单隐藏字段或 HTTP 头(如 `X-CSRF-TOKEN`)提交 Token。 3. 服务器比对请求中的 Token 与存储值,不一致则拒绝请求(返回 403)。 #### 2. **默认防护规则** - **受保护方法**:默认拦截 POST、PUT、PATCH、DELETE 请求[^1]。 - **豁免场景**:GET、HEAD、TRACE、OPTIONS 等幂等操作无需 CSRF 校验[^2]。 --- ### 三、Spring Security 中的具体实现 #### 1. **基础配置(默认开启)** ```java @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf() // 默认启用 .and() .authorizeRequests() .anyRequest().authenticated(); } } ``` #### 2. **前端整合** ##### 表单提交(Thymeleaf 示例) ```html <form action="/transfer" method="post"> <input type="hidden" name="_csrf" th:value="${_csrf.token}"/> <input type="text" name="amount"/> <button type="submit">转账</button> </form> ``` Thymeleaf 会自动注入 `_csrf` Token(需启用 `th:action` 属性)[^3]。 ##### RESTful API(手动携带 Token) ```javascript // 前端 AJAX 请求 fetch("/api/update", { method: "POST", headers: { "X-CSRF-TOKEN": getCookie("XSRF-TOKEN") // 从 Cookie 读取 Token } }); ``` #### 3. **异步请求特殊处理** 在异步请求(如 AJAX)中,需通过 HTML meta 标签传递 Token: ```html <meta name="_csrf" content="${_csrf.token}"> <meta name="_csrf_header" content="${_csrf.headerName}"> ``` JavaScript 动态获取并添加到请求头: ```javascript let token = document.querySelector('meta[name="_csrf"]').content; let header = document.querySelector('meta[name="_csrf_header"]').content; xhr.setRequestHeader(header, token); // 添加到请求头 ``` --- ### 四、常见问题与解决方案 #### 1. **登录请求被拦截** - **原因**:自定义登录页未携带 `_csrf` 参数。 - **解决**:在登录表单中添加隐藏字段: ```html <input type="hidden" name="_csrf" th:value="${_csrf.token}"/> ``` #### 2. **排除特定路径** ```java http.csrf() .ignoringAntMatchers("/api/public/**"); // 忽略公开 API ``` #### 3. **禁用 CSRF 防护(不推荐)** ```java http.csrf().disable(); // 仅用于测试或内部 API ``` --- ### 五、高级应用场景 #### 1. **Cookie 与 Session 存储模式** - **Cookie 存储**:通过 `CookieCsrfTokenRepository` 实现无状态 Token 管理。 ```java http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); ``` - **Session 存储**:默认使用 `HttpSessionCsrfTokenRepository`。 #### 2. **微服务架构中的 CSRF** - **问题**:跨服务调用时 Token 无法共享。 - **方案**:采用 JWT 或 OAuth2 替代传统 CSRF 防护。 --- ### 附:CSRF 防护流程图 ```plaintext 客户端请求 │ ├─ GET请求 → 放行(不校验Token) │ └─ POST/PUT/DELETE请求 → 检查_csrf参数 │ ├─ Token匹配 → 执行操作 │ └─ Token不匹配 → 返回403 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值