使用Anti-CSRF令牌防御CSRF攻击:最佳实践与实现方案
Acunetix | 2025年2月12日
防止跨站请求伪造(CSRF)攻击最广泛使用的方法是实施Anti-CSRF令牌。这些是由Web应用程序生成的唯一值,并在每个请求中进行验证以确保真实性。CSRF攻击利用用户的活跃会话来执行未经授权的操作,例如将用户重定向到恶意网站或访问敏感会话数据。
为了有效缓解这些风险,必须正确生成、管理和验证CSRF令牌,确保对未经授权请求的强大防护。
什么是Anti-CSRF令牌?
Anti-CSRF令牌(也称为CSRF令牌)是一种安全机制,旨在验证用户请求的合法性。它通过向用户的浏览器分配一个唯一的、不可预测的令牌来工作,该令牌必须包含在后续请求中。这确保了请求来自经过身份验证的用户,而不是攻击者。为了有效,令牌必须是密码学安全的并且抗猜测。应用程序必须在处理任何HTTP请求之前验证令牌,仅允许在用户会话内执行授权操作。
什么是跨站请求伪造(CSRF)?
跨站请求伪造(CSRF)是一种Web安全漏洞,使攻击者能够以合法用户的身份执行未经授权的操作。在典型的CSRF攻击中,攻击者诱使用户点击恶意链接或访问被入侵的网页,然后向用户已通过身份验证的Web应用程序发送伪造请求。根据目标应用程序的不同,成功的CSRF攻击可以操作用户数据、更改帐户设置或执行其他意外的状态更改操作。
虽然有多种方法可以实现Anti-CSRF保护,但基本原理保持不变:确保每个请求都来自可信源。为了更好地理解其工作原理,让我们探讨一个基本示例。
CSRF漏洞示例
假设您运营一个托管在www.example.com上的Web应用程序,没有任何CSRF保护。在此应用程序中,用户可以通过填写简单的HTML表单并单击“提交”来向其个人资料发布消息:
|
|
当用户提交表单时,其Web浏览器会向服务器发送POST请求,通过subject和content参数传输输入的数据:
|
|
如果没有CSRF保护,攻击者可以通过制作一个恶意网页来利用这一点,该网页以经过身份验证的用户的名义静默提交请求,可能在用户的会话中发布不需要的内容或执行其他意外操作。
CSRF攻击如何利用易受攻击的Web应用程序
如果用户登录到Web应用程序并且攻击者了解请求的结构,他们可以通过诱骗用户 unknowingly 提交恶意请求来利用跨站请求伪造(CSRF)。这可以通过诱使用户访问攻击者控制的网站来实现,该网站随后自动执行未经授权的操作——例如在用户的个人资料上发布广告。
例如,攻击者的站点可能包含一个隐藏表单,如下所示:
|
|
当用户访问攻击者的站点时,嵌入的JavaScript自动提交表单,使其浏览器向合法应用程序发送以下POST请求:
|
|
如果目标站点缺乏CSRF保护,该请求将被处理,就像用户有意提交的一样。由于浏览器在请求中包含了用户的会话cookie,服务器将其视为合法操作——而不验证其来源。这就是CSRF如此危险的原因:服务器假定请求是有效的,仅仅因为它来自经过身份验证的会话,无论它是在何处触发的。
实现基础CSRF令牌进行保护
为了防御CSRF攻击,可以实现一个简单的基于令牌的缓解策略。这包括在用户登录时生成一个唯一的安全令牌,并确保应用程序中的所有表单提交都包含此令牌。当正确生成和验证时,这种方法可以防止未经授权的请求被处理。
一个安全的表单实现可能如下所示:
|
|
在服务器端,只应接受包含正确CSRF令牌的POST请求。格式正确的请求可能如下所示:
|
|
通过强制执行令牌验证,服务器确保只有合法用户才能提交请求。试图通过从外部站点发送伪造请求来利用CSRF的攻击者将失败,因为他们无法预测或访问有效用户的令牌。由于服务器拒绝任何缺少正确令牌的请求,未经授权的操作被有效阻止。
安全CSRF令牌生成和验证的最佳实践
您用于生成和验证CSRF令牌的方法应与应用程序的安全要求保持一致。许多现代Web框架和编程语言提供内置的CSRF保护,利用这些功能或维护良好的外部库通常是最可靠的方法。如果您需要手动实现CSRF保护,请遵循以下关键指南以确保您的令牌有效:
- 使用密码学安全令牌 – 令牌应使用密码学算法随机生成,并且长度至少为128位以抵抗暴力攻击。
- 防止令牌重用 – 每个令牌应绑定到特定的用户会话,并在敏感操作后重新生成。在适当的时间范围内使令牌过期可以平衡安全性和可用性。
- 强制执行严格的令牌验证 – 服务器必须使用安全的比较方法(例如,密码学哈希比较)在每个请求上验证令牌,以防止操纵。
- 避免在URL或未加密流量中暴露 – 切勿通过GET请求或在未加密的HTTP流量中发送CSRF令牌。这可以防止令牌通过服务器日志、浏览器历史记录或Referrer标头泄露。
- 利用安全Cookie – 将CSRF令牌存储在SameSite cookie中有助于缓解跨站攻击。此外,使用HTTPOnly属性可以防止基于JavaScript的攻击访问令牌。
- 缓解XSS漏洞 – 跨站脚本(XSS)可能允许攻击者窃取或操纵CSRF令牌。确保您的应用程序没有XSS缺陷可以加强整体CSRF保护。
通过遵守这些最佳实践,您可以有效防止CSRF攻击,并确保只有合法的用户请求被您的应用程序处理。
实现不同级别的CSRF保护
基础的CSRF保护机制包括在用户登录时生成令牌,将其存储在他们的会话cookie中,并要求在活跃会话期间所有表单提交都需要该令牌。这种方法可能是有效的,特别是与会话过期策略结合使用时。然而,某些应用程序可能需要更健壮的方法来增强安全性。
表单特定CSRF保护
为了在保持可用性的同时提高安全性,您可以为每个表单生成一个唯一的CSRF令牌,而不是依赖单个会话范围的令牌。这种方法确保即使一个令牌被泄露,它也不能用于跨多个表单的未经授权操作。
要实现此方法:
-
在内部生成CSRF令牌,但不要直接将其暴露给用户的浏览器。
-
在将其发送到客户端之前,创建令牌与表单文件名组合的哈希版本。例如:
hash_hmac('sha256', 'post.php', $_SESSION['internal_token']); -
在服务器上通过重新生成哈希并将其与提交的值进行比较来验证令牌。如果计算出的哈希与接收到的匹配,则认为请求有效,确保使用了相同的表单进行提交。
通过实施表单特定的CSRF保护,您可以进一步降低令牌重用攻击的风险,在不影响用户体验的情况下增强整体安全性。
每请求CSRF保护
对于需要最高安全级别的应用程序,例如在线银行平台,可以实现每请求CSRF令牌策略。这种方法涉及在验证每个令牌后立即使其失效,确保每个请求都需要一个新生成的令牌。虽然高度安全,但这种方法带来了显著的可用性挑战:
- 增加的服务器负载 – 每个请求都需要生成一个新的密码学安全令牌,这可能会影响服务器性能和资源可用性。
- 有限的多标签页功能 – 由于每个请求都需要唯一的令牌,用户无法跨多个浏览器标签页与应用程序交互而不触发CSRF验证错误。
- 受限的导航 – 用户不能依赖浏览器的后退按钮进行导航,因为重新访问先前页面将尝试提交过期的令牌。相反,他们必须使用应用程序的内置导航控件。
使用双提交Cookie的无状态CSRF保护
在服务器端令牌存储不切实际的场景中,例如具有有限后端存储能力的高流量应用程序,可以使用双提交Cookie模式实现无状态CSRF保护。这种方法消除了服务器存储令牌的需要,同时仍然提供对CSRF攻击的有效防御。
双提交Cookie的工作原理
- 初始令牌生成 – 在身份验证之前,服务器生成一个随机令牌并将其存储在Cookie中。
- 令牌传输 – 每个后续请求必须在隐藏表单字段或自定义HTTP标头中包含此令牌。
- 验证 – 服务器验证从请求接收的令牌与存储在用户Cookie中的值匹配。
这种方法有两种变体:
- 基础双提交令牌(“朴素”方法) – 令牌是一个随机的、不可猜测的值。服务器只需检查Cookie存储的令牌与请求提交的令牌之间是否匹配。
- 签名双提交令牌 – 令牌使用服务器端密钥进行密码学签名,使其防篡改。一些实现通过在令牌中包含时间戳来进一步增强安全性,允许基于过期的验证。
虽然双提交Cookie减少了后端存储需求,但如果攻击者可以在用户的浏览器中执行JavaScript(例如,通过XSS漏洞),它们并不能防止CSRF攻击。因此,这种方法应与其他安全措施一起使用,例如SameSite Cookie和XSS缓解策略。
异步(Ajax)请求的CSRF保护
现代Web应用程序频繁使用Ajax请求而不是传统的表单提交,这可能会使标准CSRF令牌的实现复杂化。一个实用的替代方法是在所有需要CSRF保护的Ajax请求中包含一个自定义请求标头。此标头应包含一个唯一的键值对,该键值不与现有的HTTP标头冲突。在服务器端,任何没有预期自定义标头的传入请求都应被拒绝,以防止未经授权的操作。
但是,需要注意的是,配置错误的CORS(跨源资源共享)策略可能允许攻击者同时设置Cookie和自定义标头,可能绕过CSRF保护。为了缓解这种风险,请确保CORS设置严格限制对您控制下的可信源的访问。
示例:Ajax请求中的自动CSRF保护
为了默认强制执行CSRF保护,您可以重写JavaScript的XMLHttpRequest.open()方法,以便所有传出请求自动包含自定义的Anti-CSRF标头。许多流行的JavaScript库和框架提供内置机制来实现这一点,使得将CSRF保护集成到基于Ajax的应用程序中更加容易。
较早的安全建议有时表明API端点不需要CSRF保护。然而,随着更多应用程序现在完全由API驱动,这个建议已经过时。与Ajax请求一样,可以通过强制执行自定义请求标头来验证请求真实性并防止CSRF攻击,从而加强API安全性。
Anti-CSRF令牌对登录表单的重要性
一个常见的误解是CSRF保护仅在用户登录后才需要,导致一些人认为登录表单不需要Anti-CSRF令牌。虽然攻击者不能在身份验证前直接冒充用户,但未能保护登录过程仍然可能暴露敏感信息并导致帐户操纵。
攻击者如何利用未受保护的登录表单
- 攻击者在您的Web应用程序上创建一个帐户。
- 他们诱骗受害者使用攻击者的凭据登录——这可以通过社会工程策略实现,例如发送伪装的登录链接。
- 受害者 unknowingly 使用应用程序,但登录的是攻击者的帐户而不是他们自己的。
- 攻击者监视受害者的活动,可能获取个人数据、财务信息或跟踪用户交互。在某些情况下,他们可能能够以受害者的名义发起操作,例如使用存储的支付详细信息进行购买。
为了防止这种类型的攻击,CSRF保护也应实现在登录表单上,确保只有合法的登录尝试被处理。
CSRF保护与XSS缓解相辅相成
虽然正确实现的Anti-CSRF令牌是防御CSRF攻击的有效措施,但如果应用程序内存在其他漏洞,它们并非万无一失。特别是,跨站脚本(XSS)漏洞可以通过注入恶意脚本来绕过CSRF保护,这些脚本动态请求并提交表单,自动检索并使用有效的CSRF令牌。
为了确保强大的安全态势:
- 定期扫描和测试您的Web应用程序、API和身份验证流程中的漏洞,包括CSRF和XSS。
- 实施严格的输入验证以防止可能被利用来攻击CSRF保护的XSS攻击。
- 使用安全的编码实践和安全工具来检测和缓解威胁,然后它们才能被利用。
全面的安全方法——涵盖CSRF预防、XSS缓解和整体Web应用程序安全——对于保护用户数据和防止帐户操纵至关重要。