禁用 CSRF 保护
什么是 CSRF 保护?
Cross-Site Request Forgery (CSRF) protection is a security mechanism that prevents malicious websites from making unauthorized requests on behalf of authenticated users. Yii3 includes built-in CSRF protection through the Yiisoft\Yii\Web\Middleware\CsrfTokenMiddleware middleware.
要全面了解 CSRF 攻击和保护机制,请参阅主指南中的安全最佳实践部分。
何时禁用 CSRF 保护
虽然 Web 应用程序通常应保持启用 CSRF 保护,但在某些特定场景下你可能需要禁用它:
当外部系统无法提供 CSRF 令牌时
在构建 API 或处理来自外部系统的自动化请求时,CSRF 保护可能会干扰合法请求,因为这些系统无法提供有效的 CSRF 令牌:
- 第三方集成:外部服务无法提供有效的 CSRF 令牌
- 移动应用程序:原生移动应用通常不像 Web 浏览器那样使用 cookie 或会话
- 服务器到服务器通信:为机器对机器通信设计的 API 端点
- 支付处理器:PayPal、Stripe 和其他支付系统发送 webhook 通知
- 版本控制系统:用于 CI/CD 流水线的 GitHub、GitLab webhook
- 社交媒体平台:Twitter、Facebook webhook 通知
- 通信服务:Slack、Discord 机器人集成
如何禁用 CSRF 保护
首先,你需要从 config/web/di/application.php 中的主应用程序中间件列表中删除 CSRF 中间件:
return [
Application::class => [
'__construct()' => [
'dispatcher' => DynamicReference::to([
'class' => MiddlewareDispatcher::class,
'withMiddlewares()' => [
[
ErrorCatcher::class,
SessionMiddleware::class,
CsrfTokenMiddleware::class, // <- Remove this lineNow, if you need to leave CSRF on for specific routes or route groups, you can do so by adding the CsrfTokenMiddleware middleware to the router configuration in config/common/routes.php. For a group that would be the following:
return [
Group::create()
->middleware(CsrfTokenMiddleware::class)
->routes(对于单个路由,你可以直接将中间件添加到路由:
Route::methods([Method::GET, Method::POST], '/say[/{test}]')
->action(\App\Controller\Echo\Action::class)
->name('echo/say')
->middleware(CsrfTokenMiddleware::class)安全注意事项
禁用 CSRF 保护时,请牢记以下安全注意事项:
替代身份验证方法
对于 API 端点,实施适当的身份验证机制:
- API 密钥:要求使用 API 密钥进行身份验证
- Bearer 令牌:使用 JWT 或类似的基于令牌的身份验证
- OAuth 2.0:为第三方访问实施 OAuth 2.0
- IP 白名单:将 webhook 的访问限制为已知的 IP 地址
请求验证
为没有 CSRF 保护的请求实施额外的验证:
- 签名验证:验证 webhook 签名(例如,GitHub 的 X-Hub-Signature)
- 时间戳验证:检查请求时间戳以防止重放攻击
- 速率限制:实施速率限制以防止滥用
- 输入验证:严格验证所有输入参数
示例:Webhook 签名验证
<?php
declare(strict_types=1);
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
final class WebhookController
{
public function payment(ServerRequestInterface $request): ResponseInterface
{
// Verify webhook signature
$signature = $request->getHeaderLine('X-Webhook-Signature');
$payload = (string) $request->getBody();
$expectedSignature = hash_hmac('sha256', $payload, $this->webhookSecret);
if (!hash_equals($signature, $expectedSignature)) {
throw new \RuntimeException('Invalid webhook signature');
}
// Process webhook payload
// ...
}
}最佳实践
- 最小化暴露:仅在绝对必要时禁用 CSRF 保护
- 使用 HTTPS:始终对 API 端点和 webhook 使用 HTTPS
- 监控日志:记录所有对 API 端点的请求以进行安全监控
- 定期安全审计:定期审查你的 API 端点及其安全措施
- 文档记录:清楚地记录哪些端点禁用了 CSRF 保护以及原因
结论
虽然 CSRF 保护对 Web 应用程序至关重要,但在某些合法场景下需要禁用它,特别是对于外部 API 和 webhook。禁用 CSRF 保护时,始终实施替代安全措施并遵循安全最佳实践,以维护应用程序的整体安全性。
请记住,禁用 CSRF 保护会增加安全风险,因此仔细考虑并正确实施替代安全措施至关重要。