thecodingmachine / csrf-header-check-middleware
一个符合 PHP PSR-15 规范的中间件,用于检查 CSRF 攻击。
Requires
- php: >=7
- psr/http-server-middleware: ^1.0
Requires (Dev)
- phpunit/phpunit: ^7.0.2
- satooshi/php-coveralls: ^1.0
- zendframework/zend-diactoros: ^1.4
This package is auto-updated.
Last update: 2024-09-13 22:43:39 UTC
README
CSRF 头部检查中间件
此包包含一个符合 PHP PSR-15 规范的中间件,用于检查 CSRF 攻击。
它实现了 OWASP 对防止跨站请求伪造的一般推荐的第一条(通过标准头部验证同一来源)。
注意,OWASP 建议还使用 CSRF 令牌。这需要修改您的应用程序,并且此中间件不提供任何关于 CSRF 令牌生成的帮助。其他包(如 Slim-CSRF)可以帮助您进行 CSRF 令牌验证。
它在做什么?
CsrfHeaderCheckMiddleware
会检查所有 POST/PUT/DELETE 请求(实际上是所有非 GET/HEAD/OPTIONS 的请求)。它会验证请求的 "Origin" 是否为您的网站。
它是通过比较 "Origin"(或作为备选的 "Referrer" 头部)与您的网站域名来实现的。如果头部不匹配(或如果找不到头部),它将触发一个异常。
为什么它有效?
在 CSRF 攻击中,受害者(Alice)已登录您的应用程序。攻击者(Eve)向 Alice 发送了一个恶意网站链接。恶意网站包含一些执行您网站表单 POST 的 JavaScript 代码。由于 Alice 已登录您的网站,POST 操作成功,允许 Eve 代表 Alice 执行操作。
查询因此由 Alice 的计算机执行。我们可以预期 Alice 的浏览器会像“正常”浏览器一样表现。
正常浏览器 不允许 JavaScript 代码修改“Origin”或“Referer”头部。
与其他解决方案相比如何?
在应对 CSRF 攻击时,最常用的解决方案是在每个表单中生成一个令牌,将其存储在会话中,并检查用户是否发送了该令牌。如果您正在寻找基于 PSR-7/PSR-15 的 CSRF 令牌中间件,请查看 Ocramius/PSR7Csrf
与基于令牌的实现的优势
检查 HTTP 头部可以在中间件中完成。与基于令牌的中间件相比,您必须修改应用程序以生成令牌并将令牌与任何表单一起发送。相比之下,检查头部不需要做任何工作,只需添加中间件即可。因此,部署非常快。
限制
- 此中间件完全绕过了 GET 请求。如果您的应用程序在 GET 请求上修改状态,您将陷入困境。当然,状态修改应该仅发生在 POST 请求上(但请务必再次检查,确保修改状态的路由仅与 POST/DELETE/PUT 请求一起工作)。
- 此中间件期望 "Origin" 或 "Referer" 头部被填充。这通常是真的,除非您处于一个有代理服务器修改请求的内部环境。例如,一些代理服务器已知会剥离头部以使请求匿名。
- 将阻止 CORS 请求。如果您期望请求来自您的网站之外的来源,则不能使用此中间件。
- 如果您的网站是通过第三方应用程序(如手机应用)访问的,您不能使用此中间件,因为来源和引用将会是空的。
如果您处于这些情况之一,请使用基于令牌的中间件。
安装
composer require thecodingmachine/csrf-header-check-middleware
使用
最简单的使用方法是基于默认设置。它假设您已经配置了一个兼容PSR-7的应用程序,该应用程序支持管道中间件。
在zendframework/zend-expressive
应用程序中,设置如下所示
$app = \Zend\Expressive\AppFactory::create(); $app->pipe(\TheCodingMachine\Middlewares\CsrfHeaderCheckMiddlewareFactory::createDefault();
猜测您的域名
此中间件会尽力“猜测”您网站的域名。为此,它将检查HTTP请求的“Host”头。
您需要知道这一点
- 正常浏览器总是发送“Host”头(至少在HTTP 1.1中如此)。
- 在正常浏览器中,“Host”头不能被JavaScript代码修改。
然而
- “Host”头可以被代理修改
- 代理通常会将在前一个“Host”头放入“X-Forwarded-Host”头中
- “X-Forwarded-Host”头不可信,因为它可以从客户端(在JavaScript中)被修改
因此,如果您在代理后面运行应用程序,或者由于某些原因处理HTTP/1.0,您必须手动指定应用程序的域名。
// The first argument of the factory is a list of domain name for your application. $app->pipe(\TheCodingMachine\Middlewares\CsrfHeaderCheckMiddlewareFactory::createDefault([ 'alice.com', 'www.alice.com' ]);
禁用CSRF检查
您可以根据路由逐个禁用CSRF检查
// The second argument of the factory is a list of regular expressions that will be matched on the path. // Here, we disable CSRF checks on /api/* $app->pipe(\TheCodingMachine\Middlewares\CsrfHeaderCheckMiddlewareFactory::createDefault([], [ '#^/api/#' ]);
这对于仅用于服务器之间通信的API可能很有用。请注意,如果您决定禁用某些路由的CSRF,您需要为此路由提供其他形式的保护。
或者,任何传递给中间件并设置了'TheCodingMachine\BypassCsrf'属性的请求将被忽略
// Put this in a middleware placed before the `CsrfHeaderCheckMiddleware` to disable it. $request = $request->withAttribute('TheCodingMachine\\BypassCsrf', true);