maba / gentle-force
基于Redis,使用漏桶/令牌桶算法限制暴力破解尝试和普通请求的库
0.3.1
2021-08-26 19:46 UTC
Requires
- php: ^7.1 | ^8.0
- predis/predis: ^1.1
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpunit/phpunit: 7.5 | ^8.5 | ^9.5
- sebastian/comparator: ^3.0 | ^4.0
- symfony/stopwatch: ^4.4 | ^5.2
README
这是一个用于限制暴力破解尝试(如无效凭证)和普通请求的库。
功能
- 可用于限制暴力破解尝试;
- 可用于请求速率限制;
- 使用漏桶/令牌桶算法。这意味着用户不必等待下一小时或一天,随着时间推移可以尝试额外的次数。这也意味着当每小时开始时,请求不会大量涌入;
- 处理竞争条件。这对于暴力破解限制很重要。例如,如果有1000个请求同时发出以检查同一用户的密码,则只有配置的数量次尝试是可能的;
- 可以为单个用例配置多个限制(例如,每分钟最多100次请求,每小时最多200次);
- 不对使用位置或用途做出假设——它可以与用户标识符、API令牌、IP地址或其他任何数据一起使用,以分组使用。
安装
composer require maba/gentle-force
用法
<?php use Maba\GentleForce\RateLimit\UsageRateLimit; use Maba\GentleForce\RateLimitProvider; use Maba\GentleForce\Throttler; use Maba\GentleForce\Exception\RateLimitReachedException; $rateLimitProvider = new RateLimitProvider(); $rateLimitProvider->registerRateLimits('credentials_error', [ // allow 3 errors per hour; 2 additional errors if no errors were made during last hour (new UsageRateLimit(3, 3600))->setBucketedUsages(2), // allow 10 errors per day new UsageRateLimit(10, 3600 * 24), ]); $rateLimitProvider->registerRateLimits('api_request', [ // - allow 10 requests each minute; // - user can "save up" hour of usage if not using API. // This means up to 610 requests at once, after that - 10 requests per minute, // which could again save-up up to 610. (new UsageRateLimit(10, 60))->setBucketedPeriod(3600), ]); $throttler = new Throttler(new \Predis\Client([ 'host' => '127.0.0.1', ]), $rateLimitProvider); // rate limiting: try { $result = $throttler->checkAndIncrease('api_request', $_SERVER['REMOTE_ADDR']); header('Requests-Available', $result->getUsagesAvailable()); } catch (RateLimitReachedException $exception) { header('Wait-For', $exception->getWaitForInSeconds(), 429); return; } // brute-force limiting: try { // we must increase error count in-advance before even checking credentials // this avoids race-conditions with lots of requests $credentialsResult = $throttler->checkAndIncrease('credentials_error', $_POST['username']); } catch (RateLimitReachedException $exception) { echo sprintf('Too much password tries for user. Please try after %s seconds', $exception->getWaitForInSeconds()); return; } $credentialsValid = checkCredentials($_POST['username'], $_POST['password']); if ($credentialsValid) { // as we've increased error count in advance, we need to decrease it if everything went fine $credentialsResult->decrease(); // log user into system }
替代方案
实际上,有很多这样的替代方案。
不幸的是,由于一些提供额外的功能(如不同的存储方法:文件、memcached等),没有找到符合这些标准的方案
- 可用于暴力破解(仅在错误时),而不是所有请求;
- 抽象,以便可以按用户、IP和其他标识符进行限制;
- 速率限制算法不会长时间阻塞合法用户;
- 在高度负载下,实际限制不会出现竞争条件,从而无法正确工作。
一些已审查的替代方案:RateLimitInterface、rate-limiter、LosRateLimit、Rate-limit、rate-limit、php-ratelimiter、tokenbucket、brute-force、LoginGateBundle、tresholds-governor、throttle、PeerjUserSecurityBundle、php-ratelimiter、RateLimitBundle、CybBotDetectBunble、CCDNUserSecurityBundle、limit-number-calls-bundle、rate-limiter-php、flaps、token-bucket
语义版本控制
此库遵循语义版本控制。
请参阅 Symfony BC 规则 了解在 API 中可以更改的内容以及不能更改的内容的基本信息。
运行测试
功能测试需要 Redis 以及几个 PHP 扩展来支持进程分离,以便测试高流量下的行为。因此,通常情况下,在 Docker 中运行它们更容易。
composer update
cd docker
docker-compose up -d
docker exec -it gentle_force_test_php vendor/bin/phpunit
docker-compose down
贡献
欢迎创建问题和提交 pull request。
您可以使用以下命令修复任何代码风格问题
vendor/bin/php-cs-fixer fix --config=.php_cs