CSRF(跨站请求伪造)——一种攻击类型,攻击者迫使受害者的浏览器以授权用户身份向目标服务器发送伪造请求。根据OWASP,2026,CSRF位列Web应用程序十大最危险风险之一。在移动开发背景下,CSRF攻击对于使用Cookie认证的REST API尤其危险。跨站请求伪造尽管已实施现代防护机制,仍然是一项现实威胁。
要点
CSRF(跨站请求伪造)是一种攻击,攻击者创建伪造请求并迫使受害者的浏览器将其发送到目标服务器。服务器执行该请求,因为它收到了当前用户会话的有效Cookie凭据。攻击之所以可能,是因为浏览器自动向目标域名的每个请求添加Cookie,无论请求是从哪个页面发送的。用户可能甚至没有看到攻击者的页面——只需加载带有恶意URL的隐藏<img>、<form>或<iframe>即可。CSRF不直接窃取数据——攻击以受害者名义执行操作(状态更改操作),如转账、修改密码或删除帐户。
CSRF攻击仅针对改变状态的操作——具有副作用的GET请求、POST、PUT和DELETE。例如,在个人帐户中更改电子邮件地址的请求:如果服务器未经来源检查就接受该请求,攻击者可以替换自己的电子邮件并启动密码重置。该攻击尤其危险,针对银行系统、管理面板和社交网络,其中一次操作会造成严重后果。使用Cookie进行身份验证的移动应用程序API如果不应用额外检查,也容易受到CSRF攻击。
任何身份验证基于Cookie且服务器不检查请求来源的Web应用程序和API都会受到攻击。使用WebView通过Web表单进行授权的移动应用程序也很脆弱:浏览器组件自动发送Cookie,攻击者可以通过后台加载注入恶意请求。根据HackerOne(2025)的数据,Web应用程序中约12%的漏洞报告与CSRF防护缺失有关。
CSRF的主要特征是对受害者不可见。用户可能甚至没有怀疑攻击已经发生:伪造请求在后台执行,应用程序界面不显示入侵迹象。检测CSRF的唯一方法是监控服务器日志或帐户的突然变化。此外,CSRF易于与其他漏洞(如XSS或开放重定向)组合,从而成倍增加损害。
CSRF攻击由三个必要条件组成:受害者在目标网站上已授权,服务器使用Cookie认证,攻击者的请求指向操作URL。攻击者创建一个HTML页面,其中包含表单、脚本或图像,其src属性指向目标URL。受害者的浏览器加载此页面并自动将请求连同当前会话的Cookie一起发送到服务器。服务器收到有效的Cookie,不检查请求来源,并执行操作。
<!-- 通过隐藏表单进行 CSRF 攻击的示例 -->
<form action="https://bank.example.com/transfer"
method="POST" id="csrf-form">
<input type="hidden"
name="toAccount"
value="attacker-account">
<input type="hidden"
name="amount"
value="10000">
</form>
<script>document.getElementById("csrf-form").submit()</script>
页面加载后,脚本立即提交表单。浏览器将用户的会话Cookie附加到发送至bank.example.com的POST请求。银行服务器检查Cookie,确认用户已认证,并执行向攻击者帐户的转账。受害者看到空白或合法页面,而资金已被扣划。
HTTP协议的关键特性——缺乏内置的请求来源检查。如果请求的域名与Cookie的域名匹配,浏览器会将Cookie添加到请求中。攻击者不需要知道Cookie的内容——浏览器自动完成。同源策略不防止CSRF,因为攻击针对的是服务器,而不是读取响应。像CORS这样的机制也无能为力:CSRF请求通常不需要读取响应来造成损害。
CSRF攻击根据恶意请求的传递方式进行分类。每种类型使用不同的HTML元素发送请求,但都依赖于浏览器自动发送Cookie。方法的选择取决于攻击者的目标:基于GET的攻击需要更少的代码,基于POST的更能可靠地绕过某些防护,基于XMLHttpRequest的允许操作标头。
| 攻击类型 | 传递向量 | HTTP方法 | 检测难度 |
|---|---|---|---|
| 基于GET | <img>、<script>、<iframe> | GET | 高 |
| 基于POST | 隐藏<form>+自动提交 | POST | 中 |
| 基于XHR | 带CORS的XMLHttpRequest | 任意 | 低 |
最简单的方法:攻击者在页面上放置一个带有包含请求参数的URL的<img>。浏览器加载图像并向服务器发送GET请求。例如,<img src="https://api.example.com/delete?postId=123" />如果服务器通过GET处理DELETE,则会删除记录。尽管存在明显危险,一些API仍然使用GET进行删除或更新操作。
如果服务器只接受POST请求,攻击者创建一个具有POST方法的隐藏表单,并通过JavaScript自动提交。表单不在屏幕上显示(所有<input>都有type="hidden"),autofocus + .submit()无需用户点击即可触发。如果服务器检查Content-Type标头,基于POST的攻击就无法工作,但大多数API接受标准的application/x-www-form-urlencoded。
XMLHttpRequest或Fetch API允许发送带有任意标头的请求。如果服务器配置了过于宽泛的CORS(Access-Control-Allow-Origin: *),攻击者可以发送任何请求并读取响应。然而,对于CSRF攻击,读取响应并非必需——执行操作就足够了。现代浏览器在非标准请求之前发送OPTIONS预检请求,如果服务器配置正确,这可能会阻止基于XHR的CSRF。
移动应用程序比网站更不容易受到CSRF攻击,因为原生应用程序很少使用Cookie认证。相反,移动API更常在Authorization标头中使用令牌(Bearer令牌、JWT)。然而,在某些场景下CSRF攻击是可能的:带有Web登录的WebView、混合应用程序以及带有基于Cookie的会话的API。根据TechCrunch(2025)的数据,约18%的移动应用程序公共API仍然支持会话Cookie。
许多应用程序在WebView中打开网页——通过OAuth授权、支付表单、查看内容。WebView是应用程序内部的一个完整浏览器,存储会话Cookie。如果攻击者找到在WebView中加载自己URL的方法(通过开放重定向或深度链接),就可以像在普通浏览器中一样执行CSRF攻击。防护——对于关键操作,使用Chrome Custom Tabs或SFSafariViewController代替WebView。
JWT令牌通常存储在localStorage或应用程序内存中,不会自动发送——开发人员明确为每个请求添加Authorization标头。这使得经典的CSRF攻击成为不可能。但是,如果应用程序将JWT存储在Cookie中(罕见但存在),风险就会回归。额外防护——通过azp或aud声明将JWT绑定到特定的请求来源,这可以防止令牌在其他域名上使用。
// 在Express中服务器端验证CSRF令牌的示例
const csrfProtection = (req, res, next) => {
const token = req.headers['x-csrf-token'];
if (!token || token !== req.session.csrfToken) {
return res.status(403).json({ error: 'CSRF validation failed' });
}
next();
};
// 登录时生成CSRF令牌
app.post('/api/login', (req, res) => {
const csrfToken = crypto.randomBytes(32).toString('hex');
req.session.csrfToken = csrfToken;
res.json({ csrfToken: csrfToken });
});
现代CSRF防护基于三个层次:服务器端CSRF令牌、Cookie的SameSite属性和Origin标头验证。这些方法的组合提供了针对99%的CSRF攻击的防护,同时不会对用户体验产生重大影响。具体方法的选择取决于应用程序的架构:网站可以使用SameSite=Lax,移动应用程序的API需要标头中的令牌。
标准方法:服务器生成唯一令牌,将其绑定到用户会话并传递给客户端。客户端在每个更改状态的请求中包含该令牌(在隐藏表单字段或X-CSRF-Token标头中)。服务器将接收到的令牌与会话中存储的令牌进行比较。令牌必须是密码学安全的、随机的、长度至少32字节,并在每个会话或操作中更改。令牌的生命周期——不超过几个小时。
Cookie的SameSite属性限制跨域请求时发送Cookie。Lax值仅允许在顶级导航GET请求中发送Cookie——这对于大多数网站来说已经足够。Strict阻止所有跨域请求的Cookie,包括导航:用户从其他网站切换时需要重新登录。根据Chrome Platform Status(2026),SameSite=Lax在所有现代浏览器中默认启用,这将CSRF攻击的数量减少了67%。
服务器可以检查传入请求的Origin或Referer标头。如果请求来自其他域名——将被阻止。Origin比Referer更可靠,因为它始终存在于POST请求中,并且不会被浏览器策略禁用。实现:允许的来源白名单,与标头的当前值进行比较。该方法很有效,但对于移动应用程序来说较复杂,因为Origin标头可能缺失或被伪造。
// 在Spring Boot中验证CSRF令牌的示例
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
fun securityFilterChain(
@Autowired http: HttpSecurity
): SecurityFilterChain {
return http
.csrf { it.csrfTokenRepository(
CookieCsrfTokenRepository.withHttpOnlyFalse()
) }
.sessionManagement {
it.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
}
.build()
}
}
一种不需要在服务器上存储令牌的方法:服务器设置一个带有随机值的Cookie,客户端从Cookie中读取值并将其发送回标头或请求体中。服务器比较两个值。如果攻击者无法读取Cookie(同源策略),则无法伪造令牌。该方法比同步器更容易实现,但需要HTTPS来保护Cookie免受拦截。
CSRF和XSS是经常被混淆的不同类型攻击。CSRF利用服务器对用户浏览器的信任:服务器执行攻击者的命令,因为请求带有有效的Cookie。XSS利用浏览器对服务器内容的信任:浏览器执行攻击者注入到页面中的脚本。CSRF不需要在目标网站上注入代码——只需从其他域名发送请求即可。而XSS则需要找到将自身JavaScript注入页面HTML代码的方法。此外,XSS可以绕过CSRF防护:注入的脚本从页面读取CSRF令牌并将其与请求一起发送。
| 特征 | CSRF | XSS |
|---|---|---|
| 攻击目标 | 服务器 | 客户端(浏览器) |
| 向量 | 伪造请求 | 注入脚本 |
| 受害者网站上需要JavaScript吗? | 否 | 是 |
| 数据窃取 | 否(仅操作) | 是 |
| 防护 | CSRF令牌、SameSite、Origin | 输出转义、CSP |
理解CSRF和XSS之间的区别对于构建多层次防护至关重要。CSRF令牌不能防止XSS,CSP(内容安全策略)不能防止CSRF。只有方法的组合才能确保应用程序抵御这两种类型的攻击。在带有WebView的移动应用中,风险加倍,因此建议开发人员至少为API请求使用CSRF令牌,为Web内容使用内容安全策略。
常见问题解答
CSRF迫使服务器以用户身份执行操作,而XSS将恶意脚本注入受害者的浏览器。CSRF不需要在目标网站上注入代码——只需从其他域名发送请求即可。与CSRF不同,XSS可以窃取数据和读取页面内容。
检查是否使用Cookie认证以及是否存在状态更改操作的请求来源检查。如果API在没有CSRF令牌、Origin检查或SameSite的情况下接受POST/PUT/DELETE——应用程序容易受到攻击。使用OWASP ZAP或Burp Suite进行自动扫描。
不,CORS不能防止CSRF。CORS是安全读取跨域响应的机制,而CSRF攻击不需要读取响应——只需发送请求即可。通过<form>或<img>的CSRF请求不受CORS限制。
如果API使用Cookie认证——是,CSRF防护是强制性的。如果API在Authorization标头中使用Bearer令牌,CSRF风险最小,因为令牌不会由浏览器自动发送。但对于带有WebView的混合应用程序,仍然建议进行防护。
自2020年起,所有现代浏览器都支持SameSite。对于旧版浏览器,使用CSRF令牌作为主要防护方法。CSRF令牌+SameSite的组合即使在旧版浏览器中禁用SameSite的情况下也能提供最大保护。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。