phpgt/Csrf
GitHub: phpgt/Csrf
为 PHP Web 应用提供全自动的 CSRF 令牌生成、表单注入与请求验证,让开发者用最少的代码抵御跨站请求伪造攻击。
Stars: 7 | Forks: 7
# 自动防御跨站请求伪造。
本库会自动为您处理 [CSRF 防御](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)),包括生成令牌、将其注入页面中的所有表单,然后在每次接收到 POST 请求时验证是否存在有效令牌。
## 用法:三步实现防御
CSRF 库主要做两件事:
* 将 CSRF 令牌注入到 `form` 中
* 验证 `POST` 请求以确保其包含有效令牌
每项操作仅需一次方法调用,但您需要先进行设置。
### 第一步:设置
首先创建 TokenStore。目前有两种实现——`ArrayTokenStore` 和 `SessionTokenStore`。`ArrayTokenStore` 是最基础的实现,不会以任何方式进行持久化,但可以被扩展用于自定义集成。`SessionTokenStore` 是一个内置实现,可以在请求之间持久化令牌,从而使得为一个页面请求生成的令牌可以在另一个请求中被验证。添加 CSRF 防御最简单的方法是使用 Session:
```
use GT\Csrf\SessionTokenStore;
// $session is an object-oriented representation of $_SESSION
// that implements the Gt\Session\SessionContainer Interface.
$tokenStore = new SessionTokenStore($session);
```
### 第二步:验证
在运行任何其他代码(尤其是可能影响数据的操作)之前,您应该检查在需要时是否已存在有效的 CSRF 令牌:
```
use GT\Csrf\Exception\CsrfException;
if(this_is_a_post_request()) {
try {
$tokenStore->verify();
}
catch(CsrfException $e) {
// Stop processing this request and get out of there!
}
}
```
如果请求包含 POST 数据但没有有效的 CSRF 令牌,将会抛出 `CSRFException` —— 因此您应该做好捕获它的准备。请记住,如果发生这种情况,说明该请求是伪造的,因此您不应处理它!
### 第三步:为下次提交注入令牌
最后,一旦您处理完 HTML 代码并准备将其发送回客户端,您应该注入 CSRF 令牌。如果不这样做,当页面提交时,将无法通过第二步的验证!
```
use GT\Csrf\HTMLDocumentProtector;
// The html can come in as anything accepted by Gt\Dom\HTMLDocument - here it's a
// plain string in a variable.
$html = "...";
// Now do the processing.
$protector = new HTMLDocumentProtector($html, $tokenStore);
$protector->protect();
// Output the HTML of the document - you will see the new fields have
// been automatically injected.
echo $protector->getHTMLDocument();
```
## 使用不同长度的令牌
默认情况下,会生成 32 个字符的令牌。它们使用的字符集为 [a-zA-Z0-9],这意味着一个 64 位的令牌,暴力破解攻击者以每秒 100,000 次请求的速度大约需要 293 万年才能猜中。如果您觉得这个长度过长或不足,可以使用 `TokenStore::setTokenLength()` 更改令牌长度。
## 关于客户端请求的特别说明
请注意,如果您的页面上有多个表单,系统将为每个表单生成并注入一个唯一的令牌。当使用客户端请求(XMLHTTPRequest 或 Fetch,又称 AJAX)提交表单时,响应将包含一个新的令牌,必须在页面中刷新该令牌以备下次提交使用。
如果您希望每个页面只有一个令牌,并在所有表单之间共享,可以通过将 TOKEN_PER_PAGE 参数传递给 projectAndInject 方法来进行配置:`$page->protectAndInject(HTMLDocumentProtector::TOKEN_PER_PAGE);`。
每个页面存储一个令牌将减少所需的服务器资源量,但并发的客户端请求将会失败,这就是为什么默认采用每个表单一个令牌的原因。
## Session 之外的其他令牌存储方案
本包包含了 `ArrayTokenStore`,可以存储在 Session 中。您可以通过继承 `TokenStore` 并实现其抽象方法,来构建如 RDBMS 或 NoSQL 等其他备选的令牌存储方式。
# 骄傲地获得赞助
[JetBrains 开源赞助计划](https://www.jetbrains.com/community/opensource/)
[](https://www.jetbrains.com/community/opensource/)
标签:CISA项目, CSRF防护, OpenVAS, PHP, Session, Syscall, Token验证, Web安全, Web开发, YAML, 中间件, 会话管理, 多模态安全, 安全库, 开源库, 搜索引擎爬虫, 网络安全, 蓝队分析, 表单保护, 跨站请求伪造, 防御机制, 隐私保护