Skip to content

Commit a7e66ec

Browse files
authored
Merge pull request #163 from cakephp/rate-limiting
add rate limiting
2 parents 28685f6 + 9800063 commit a7e66ec

21 files changed

Lines changed: 525 additions & 31 deletions

‎Dockerfile‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ RUN apt-get update \
1212
&& apt-get update \
1313
&& apt-get install -y --no-install-recommends nodejs \
1414
&& docker-php-ext-install intl pdo_mysql zip \
15+
&& pecl install redis \
16+
&& docker-php-ext-enable redis \
1517
&& a2enmod rewrite headers expires \
1618
&& sed -ri "s!/var/www/html!${APACHE_DOCUMENT_ROOT}!g" /etc/apache2/sites-available/000-default.conf /etc/apache2/apache2.conf \
1719
&& rm -rf /var/lib/apt/lists/*

‎README.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22

33
This repository contains the code responsible for https://plugins.cakephp.org
44

5+
## Production request protection
6+
7+
The public package listing has query validation and an IP-based rate limit. It
8+
uses Dokku's `REDIS_URL` by default. `RATE_LIMIT_CACHE_URL` can override it if
9+
you later want a dedicated Redis database or service.
10+
11+
The container includes the PHP Redis extension. `TRUSTED_PROXY_IPS` must list
12+
only proxies that overwrite `X-Forwarded-For`; for the current Dokku setup its
13+
default is `172.17.0.1`. Set `RATE_LIMIT_ENABLED=false` only for temporary
14+
maintenance or local development. The defaults are 90 listing views/minute,
15+
15 filtered listings/minute, and 30 autocomplete requests/minute per client IP.
16+
517
## Starting local development
618

719
You need [DDEV](https://docs.ddev.com/en/stable/) installed and configured on your machine.

‎composer.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
"illuminate": "bin/cake illuminate code",
6666
"phpstan": "phpstan analyse",
6767
"phpstan-baseline": "phpstan --generate-baseline",
68-
"rector-setup": "cp composer.json composer.backup && composer require --dev rector/rector:\"~2.3.1\" && mv composer.backup composer.json",
68+
"rector-setup": "cp composer.json composer.backup && composer require --dev rector/rector:\"~2.6.1\" && mv composer.backup composer.json",
6969
"rector-check": "vendor/bin/rector process --dry-run",
7070
"rector-fix": "vendor/bin/rector process"
7171
}

‎composer.lock‎

Lines changed: 61 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎config/app.php‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
use Cake\Cache\Engine\FileEngine;
46
use Cake\Database\Connection;
57
use Cake\Database\Driver\Mysql;
68
use Cake\Log\Engine\FileLog;
79
use Cake\Mailer\Transport\MailTransport;
810
use function Cake\Core\env;
911

12+
$rateLimitCacheUrl = env('RATE_LIMIT_CACHE_URL', env('REDIS_URL'));
13+
$rateLimitEnabled = filter_var(
14+
env('RATE_LIMIT_ENABLED', $rateLimitCacheUrl !== null),
15+
FILTER_VALIDATE_BOOL,
16+
);
17+
$trustedProxyIps = array_values(array_filter(array_map(
18+
trim(...),
19+
explode(',', (string)env('TRUSTED_PROXY_IPS', '172.17.0.1')),
20+
)));
21+
1022
return [
1123
/*
1224
* Debug Level:
@@ -112,6 +124,23 @@
112124
],
113125
],
114126

127+
/*
128+
* Protection for the public package listing and autocomplete endpoints.
129+
*
130+
* RATE_LIMIT_CACHE_URL must be a redis:// DSN. The rate limiter remains
131+
* disabled until that shared, atomic cache has been configured.
132+
*/
133+
'PublicRequestProtection' => [
134+
'rateLimitEnabled' => $rateLimitEnabled && is_string($rateLimitCacheUrl) && str_starts_with($rateLimitCacheUrl, 'redis://'),
135+
'trustedProxyIps' => $trustedProxyIps,
136+
'maxFilterValues' => max(1, (int)env('MAX_PACKAGE_FILTER_VALUES', 3)),
137+
'rateLimits' => [
138+
'browse' => ['limit' => 90, 'window' => 60],
139+
'filtered' => ['limit' => 15, 'window' => 60],
140+
'autocomplete' => ['limit' => 30, 'window' => 60],
141+
],
142+
],
143+
115144
/*
116145
* Configure the cache adapters.
117146
*/
@@ -122,6 +151,14 @@
122151
'url' => env('CACHE_DEFAULT_URL'),
123152
],
124153

154+
'rate_limit' => [
155+
'className' => FileEngine::class,
156+
'path' => CACHE . 'rate_limit' . DS,
157+
'duration' => '+1 minute',
158+
'prefix' => 'plugins_rate_limit_',
159+
'fallback' => false,
160+
] + ($rateLimitCacheUrl ? ['url' => $rateLimitCacheUrl] : []),
161+
125162
/*
126163
* Configure the cache used for general framework caching.
127164
* Translation cache files are stored with this configuration.

‎config/app_local.example.php‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
use function Cake\Core\env;
46

57
/*

‎config/paths.php‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<?php
2+
declare(strict_types=1);
3+
24
/**
35
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
46
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)

‎config/plugins.php‎

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
<?php
2+
3+
declare(strict_types=1);
4+
25
/**
36
* Plugin configuration.
47
*
@@ -18,15 +21,14 @@
1821
* @since 5.0.0
1922
* @license https://opensource.org/licenses/mit-license.php MIT License
2023
*/
21-
22-
/*
23-
* List of plugins to load in the form `PluginName` => `[configuration options]`.
24-
*
25-
* Available options:
26-
* - onlyDebug: Load the plugin only in debug mode. Default false.
27-
* - onlyCli: Load the plugin only in CLI mode. Default false.
28-
* - optional: Do not throw an exception if the plugin is not found. Default false.
29-
*/
24+
/*
25+
* List of plugins to load in the form `PluginName` => `[configuration options]`.
26+
*
27+
* Available options:
28+
* - onlyDebug: Load the plugin only in debug mode. Default false.
29+
* - onlyCli: Load the plugin only in CLI mode. Default false.
30+
* - optional: Do not throw an exception if the plugin is not found. Default false.
31+
*/
3032
return [
3133
'DebugKit' => ['onlyDebug' => true],
3234
'Bake' => ['onlyCli' => true, 'optional' => true],

‎config/routes.php‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<?php
2+
declare(strict_types=1);
3+
24
/**
35
* Routes configuration.
46
*

‎rector.php‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@
6060
CompactToVariablesRector::class,
6161
SplitDoubleAssignRector::class,
6262
ChangeOrIfContinueToMultiContinueRector::class,
63-
ExplicitBoolCompareRector::class,
6463
NewlineBeforeNewAssignSetRector::class,
6564
DisallowedEmptyRuleFixerRector::class,
6665
RemoveUselessParamTagRector::class,

0 commit comments

Comments
 (0)