maba/gentle-force

基于Redis,使用漏桶/令牌桶算法限制暴力破解尝试和普通请求的库

0.3.1 2021-08-26 19:46 UTC

This package is auto-updated.

Last update: 2024-08-27 02:29:39 UTC


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和其他标识符进行限制;
  • 速率限制算法不会长时间阻塞合法用户;
  • 在高度负载下,实际限制不会出现竞争条件,从而无法正确工作。

一些已审查的替代方案:RateLimitInterfacerate-limiterLosRateLimitRate-limitrate-limitphp-ratelimitertokenbucketbrute-forceLoginGateBundletresholds-governorthrottlePeerjUserSecurityBundlephp-ratelimiterRateLimitBundleCybBotDetectBunbleCCDNUserSecurityBundlelimit-number-calls-bundlerate-limiter-phpflapstoken-bucket

语义版本控制

此库遵循语义版本控制

请参阅 Symfony BC 规则 了解在 API 中可以更改的内容以及不能更改的内容的基本信息。

运行测试

Travis status

功能测试需要 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