diff --git a/cfg/conf.sample.php b/cfg/conf.sample.php index e958c88d..0837645b 100644 --- a/cfg/conf.sample.php +++ b/cfg/conf.sample.php @@ -135,6 +135,10 @@ markdown = "Markdown" ; Set this to 0 to disable rate limiting. limit = 10 +; Set ips (v4|v6) which should be exempted for the rate-limit. CIDR also supported. Needed to be comma separated. +; Unset for enabling and invalid values will be ignored +; eg: exemptedIp = '1.2.3.4,10.10.10/24' + ; (optional) if your website runs behind a reverse proxy or load balancer, ; set the HTTP header containing the visitors IP address, i.e. X_FORWARDED_FOR ; header = "X_FORWARDED_FOR" diff --git a/composer.json b/composer.json index 899c863f..ff79af99 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,8 @@ "require" : { "php" : "^5.6.0 || ^7.0 || ^8.0", "paragonie/random_compat" : "2.0.19", - "yzalis/identicon" : "2.0.0" + "yzalis/identicon" : "2.0.0", + "mlocati/ip-lib": "^1.14" }, "require-dev" : { "phpunit/phpunit" : "^4.6 || ^5.0" @@ -39,4 +40,4 @@ "config" : { "autoloader-suffix" : "DontChange" } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 80d2276f..66a9c8a6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,76 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9d110873bf15a6abd66734e8a818134c", + "content-hash": "8138b0e4bb3fcaab9ca4c02bbe35d891", "packages": [ + { + "name": "mlocati/ip-lib", + "version": "1.14.0", + "source": { + "type": "git", + "url": "https://github.com/mlocati/ip-lib.git", + "reference": "882bc0e115970a536b13bcfa59f312783fce08c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mlocati/ip-lib/zipball/882bc0e115970a536b13bcfa59f312783fce08c8", + "reference": "882bc0e115970a536b13bcfa59f312783fce08c8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "ext-pdo_sqlite": "*", + "phpunit/dbunit": "^1.4 || ^2 || ^3 || ^4", + "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "IPLib\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michele Locati", + "email": "mlocati@gmail.com", + "homepage": "https://github.com/mlocati", + "role": "Author" + } + ], + "description": "Handle IPv4, IPv6 addresses and ranges", + "homepage": "https://github.com/mlocati/ip-lib", + "keywords": [ + "IP", + "address", + "addresses", + "ipv4", + "ipv6", + "manage", + "managing", + "matching", + "network", + "networking", + "range", + "subnet" + ], + "funding": [ + { + "url": "https://github.com/sponsors/mlocati", + "type": "github" + }, + { + "url": "https://paypal.me/mlocati", + "type": "other" + } + ], + "time": "2020-12-31T11:30:02+00:00" + }, { "name": "paragonie/random_compat", "version": "v2.0.19", diff --git a/lib/Configuration.php b/lib/Configuration.php index 2a326caf..133059d3 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -82,6 +82,7 @@ class Configuration 'limit' => 10, 'header' => null, 'dir' => 'data', + 'exemptedIp' => null, ), 'purge' => array( 'limit' => 300, diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index a16cd0b3..31f107ff 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -1,4 +1,5 @@ getKey('limit', 'traffic')); self::setPath($conf->getKey('dir', 'traffic')); + self::setExemptedIp($conf->getKey('exemptedIp', 'traffic')); + if (($option = $conf->getKey('header', 'traffic')) !== null) { $httpHeader = 'HTTP_' . $option; if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) { @@ -99,6 +124,32 @@ class TrafficLimiter extends AbstractPersistence if (self::$_limit < 1) { return true; } + + // Check if $_ipKey is exempted from ratelimiting + if (!is_null(self::$_exemptedIp)) { + $exIp_array = explode(",", self::$_exemptedIp); + foreach ($exIp_array as $ipRange) { + // Match $_ipKey to $ipRange and if it matches it will return with a true + $address = \IPLib\Factory::addressFromString($_SERVER[self::$_ipKey]); + $range = \IPLib\Factory::rangeFromString(trim($ipRange)); + // If $range is null something went wrong (possible invalid ip given in config) + if ($range == null) { + $contained = false; + } else { + // Ip-lib does throws and exception when something goes wrong, if so we want to catch it and set contained to false + try { + $contained = $address->matches($range); + } catch (Exception $e) { + // If something is wrong with matching the ip, we set $contained to false + $contained = false; + } + } + // Matches return true! + if ($contained == true) { + return true; + } + } + } $file = 'traffic_limiter.php'; if (self::_exists($file)) {