phpgt/csrf

自动防御跨站请求伪造。

资助包维护!
PhpGt

v1.9.0 2022-09-23 12:03 UTC

README

此库自动为您处理 CSRF 保护,包括生成令牌,将其注入页面中的所有表单,并在收到 POST 请求时验证是否存在有效的令牌。

Build status Code quality Code coverage Current version PHP.Gt/Csrf documentation

使用方法:三步保护

CSRF 库做两件事

  • 将 CSRF 令牌注入到 form
  • 验证 POST 请求以确保它们包含有效的令牌

每一步都只需要一个方法调用,但您需要先进行设置。

第一步:设置

首先创建 TokenStore。目前有两种实现——ArrayTokenStoreSessionTokenStore。《ArrayTokenStore》是最基本的,不会以任何方式持久化,但可以扩展到自定义集成。《SessionTokenStore》是一个内置实现,可以在请求之间持久化令牌,以便生成的令牌可以在另一个请求上检查。添加 CSRF 保护的最简单方法是使用会话。

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 = "<html>...</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 个请求的暴力攻击者需要大约 2.93 亿年才能猜出来。如果这看起来过多或不足,您可以使用 TokenStore::setTokenLength() 改变令牌长度。

关于客户端请求的特别说明

请注意,如果您的页面上有多个表单,将为每个表单生成一个唯一的令牌并将其注入。当使用客户端请求(XMLHTTPRequest 或 Fetch,即 AJAX)提交表单时,响应将包含一个新的令牌,该令牌必须在页面中刷新以供下一次提交使用。

如果您希望每个页面只有一个令牌,跨所有表单共享,可以通过将 TOKEN_PER_PAGE 参数传递给 projectAndInject 方法进行配置:$page->protectAndInject(HTMLDocumentProtector::TOKEN_PER_PAGE);

每个页面存储一个令牌将减少服务器资源的需求,但并发客户端请求将失败,这就是为什么默认情况下每个表单只有一个令牌的原因。

存储令牌在会话中的替代方案

此包包含一个 ArrayTokenStore,可以存储在会话中。您可以通过扩展 TokenStore 并实现抽象方法来实现替代令牌存储,例如 RDBMS 或 NoSQL。