薛定谔有只喵 發表於 2025-12-31 11:22:00

springboot~传统WEB应用开启CSRF

<h1 id="csrf-是什么"><strong>CSRF 是什么?</strong></h1>
<p><strong>CSRF(Cross-Site Request Forgery,跨站请求伪造)</strong> 是一种常见的Web安全漏洞。攻击者利用受害者已经登录的<strong>合法会话</strong>,诱使受害者执行非本意的操作。</p>
<h3 id="简单比喻"><strong>简单比喻:</strong></h3>
<blockquote>
<p>想象你在咖啡店会员卡里有钱,你每次消费只需要说“用会员卡支付”。攻击者伪装成服务员,在你面前说“用会员卡转账100元到XXX账户”。因为你已经在咖啡店的系统中“登录”了(身份已认证),系统就会执行这个操作。</p>
</blockquote>
<h2 id="csrf攻击原理"><strong>CSRF攻击原理:</strong></h2>
<h3 id="攻击流程"><strong>攻击流程:</strong></h3>
<ol>
<li><strong>用户登录</strong>:用户登录正常的网站A(如银行网站),获得登录凭证(Cookie/Session)</li>
<li><strong>用户访问恶意网站</strong>:用户在同一个浏览器中访问了攻击者的网站B</li>
<li><strong>恶意请求</strong>:网站B通过隐藏表单、图片src、AJAX等方式,向网站A发送请求</li>
<li><strong>自动携带凭证</strong>:浏览器会自动携带网站A的Cookie</li>
<li><strong>网站A执行请求</strong>:网站A看到合法Cookie,误以为是用户的自愿操作</li>
</ol>
<h3 id="攻击示例"><strong>攻击示例:</strong></h3>
<pre><code class="language-html">&lt;!-- 恶意网站上的代码 --&gt;
&lt;img src="https://your-bank.com/transfer?to=hacker&amp;amount=10000" width="0" height="0" /&gt;

&lt;!-- 或隐藏表单 --&gt;
&lt;form action="https://your-bank.com/change-email" method="POST"&gt;
&lt;input type="hidden" name="email" value="hacker@evil.com"&gt;
&lt;/form&gt;
&lt;script&gt;document.forms.submit();&lt;/script&gt;
</code></pre>
<h2 id="为什么有时要禁用csrf保护"><strong>为什么有时要禁用CSRF保护?</strong></h2>
<h3 id="适用禁用场景"><strong>适用禁用场景:</strong></h3>
<ol>
<li>
<p><strong>纯API服务(无浏览器交互)</strong></p>
<pre><code class="language-bash"># 移动App通过API访问后端
# 使用Token认证(JWT/OAuth),而不是Session
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
</code></pre>
</li>
<li>
<p><strong>仅提供非状态改变的操作</strong></p>
<pre><code class="language-http">GET /api/users          # 只读操作,通常不需要CSRF
POST /api/transfer      # 写操作,需要CSRF
</code></pre>
</li>
<li>
<p><strong>微服务内部通信</strong></p>
<pre><code class="language-yaml"># 服务间调用使用服务凭证,而不是用户会话
service-to-service: true
</code></pre>
</li>
<li>
<p><strong>某些特殊框架/场景</strong></p>
<pre><code class="language-javascript">// GraphQL通常使用token而不是session
// 或某些实时通信应用
</code></pre>
</li>
</ol>
<h3 id="具体框架中的禁用示例"><strong>具体框架中的禁用示例:</strong></h3>
<p><strong>Spring Security:</strong></p>
<pre><code class="language-java">@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
      http
            .csrf().disable()// 禁用CSRF
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .httpBasic();// 使用HTTP Basic认证
    }
}
</code></pre>
<p><strong>Django:</strong></p>
<pre><code class="language-python"># settings.py
CSRF_COOKIE_SECURE = False# 禁用CSRF
# 或针对特定视图
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def api_view(request):
    pass
</code></pre>
<h2 id="csrf保护机制对比"><strong>CSRF保护机制对比:</strong></h2>
<table>
<thead>
<tr>
<th>保护机制</th>
<th>工作原理</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>CSRF Token</strong></td>
<td>服务器生成Token,表单/请求必须携带</td>
<td>传统Web应用</td>
</tr>
<tr>
<td><strong>SameSite Cookie</strong></td>
<td>Cookie仅在同站请求中发送</td>
<td>现代浏览器支持</td>
</tr>
<tr>
<td><strong>双重Cookie验证</strong></td>
<td>客户端读取Cookie并附加到请求</td>
<td>兼容性较好</td>
</tr>
<tr>
<td><strong>Referer检查</strong></td>
<td>检查请求来源</td>
<td>简单但不可靠</td>
</tr>
<tr>
<td><strong>无(禁用)</strong></td>
<td>不验证</td>
<td>纯API、内部服务</td>
</tr>
</tbody>
</table>
<h2 id="何时应该启用禁用csrf"><strong>何时应该启用/禁用CSRF?</strong></h2>
<h3 id="应该启用csrf的场景"><strong>应该启用CSRF的场景:</strong></h3>
<ul>
<li>✅ 传统的基于Session的Web应用</li>
<li>✅ 用户通过浏览器访问的表单提交</li>
<li>✅ 需要用户交互的操作(转账、修改数据)</li>
<li>✅ 使用Cookie/Session进行身份认证</li>
</ul>
<h3 id="可以禁用csrf的场景"><strong>可以禁用CSRF的场景:</strong></h3>
<ul>
<li>✅ 纯REST API,使用JWT/OAuth Token认证</li>
<li>✅ 仅限移动App访问的后端服务</li>
<li>✅ 微服务间的内部通信</li>
<li>✅ 只读的公共API</li>
<li>✅ 使用其他认证方式(API Key、HMAC签名)</li>
</ul>
<h2 id="禁用csrf后的替代安全方案"><strong>禁用CSRF后的替代安全方案:</strong></h2>
<pre><code class="language-yaml"># 替代方案示例:

1. JWT Token认证:
   每次请求携带: Authorization: Bearer &lt;token&gt;
   
2. API Key + Secret:
   请求签名: X-Signature: sha256(api_secret + request_data)
   
3. OAuth 2.0:
   使用Access Token进行授权
   
4. CORS限制:
   Access-Control-Allow-Origin: https://trusted-domain.com
   
5. Rate Limiting:
   限制请求频率防止滥用
</code></pre>
<h2 id="最佳实践建议"><strong>最佳实践建议:</strong></h2>
<pre><code class="language-java">// 在Spring中,可以针对不同端点配置不同的CSRF策略
public class SecurityConfig {
    protected void configure(HttpSecurity http) {
      http
            // 对Web页面启用CSRF
            .csrf(csrf -&gt; csrf
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
            )
                .requireCsrfProtectionMatcher(
                  new RequestMatcher() {
                        public boolean matches(HttpServletRequest request) {
                            // 仅对特定路径启用CSRF
                            return request.getRequestURI().startsWith("/web/");
                        }
                  })
            .and()
            // API端点使用无状态认证
            .authorizeRequests()
                .antMatchers("/api/**").authenticated()
                .and()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS); // API无状态
    }
}
</code></pre>
<h2 id="验证-csrf-是否生效"><strong>验证 CSRF 是否生效</strong></h2>
<ol>
<li><strong>检查 Cookie</strong>:登录后查看浏览器是否有 <code>XSRF-TOKEN</code> Cookie</li>
<li><strong>测试请求</strong>:
<ul>
<li>直接发送 POST 请求应该被拒绝(403 错误)</li>
<li>携带正确的 CSRF Token 的请求应该成功</li>
</ul>
</li>
<li><strong>检查响应头</strong>:某些配置下,响应头会包含 CSRF Token</li>
</ol>
<pre><code class="language-bash"># 使用 curl 测试
# 1. 先获取 CSRF Token(从登录后的 Cookie 或响应头)
# 2. 发送带 CSRF Token 的请求
curl -X POST http://localhost:8080/api/test \
-H "Content-Type: application/json" \
-H "X-XSRF-TOKEN: YOUR_CSRF_TOKEN" \
-H "Cookie: XSRF-TOKEN=YOUR_CSRF_TOKEN" \
-d '{"data": "test"}'
</code></pre>
<h2 id="总结"><strong>总结:</strong></h2>
<p><strong>禁用CSRF的前提条件:</strong></p>
<ol>
<li>应用不使用<strong>Cookie/Session</strong>进行身份认证</li>
<li>请求来源可控(如仅限移动App、内部服务)</li>
<li>已实施<strong>同等或更强的安全措施</strong>替代</li>
<li>确认攻击面不会因此扩大</li>
</ol>
<p><strong>黄金法则:</strong></p>
<blockquote>
<p>如果用户通过<strong>浏览器</strong>访问你的网站,并且网站使用了<strong>Cookie/Session</strong>,那么<strong>永远不要禁用CSRF保护</strong>。只有在完全理解风险并有替代方案时,才考虑为API服务禁用CSRF。</p>
</blockquote>


</div>
<div id="MySignature" role="contentinfo">
    <p></p>
<div class="navgood">
<p>作者:仓储大叔,张占岭,<br>
荣誉:微软MVP<br>QQ:853066980</p>

<p><strong>支付宝扫一扫,为大叔打赏!</strong>
<br><img src="https://images.cnblogs.com/cnblogs_com/lori/237884/o_IMG_7144.JPG"></p>
</div><br><br>
来源:https://www.cnblogs.com/lori/p/19425698
頁: [1]
查看完整版本: springboot~传统WEB应用开启CSRF