Merge: trusted-IP throttle exemption + session hardening
This commit is contained in:
commit
41f1b941fa
3 changed files with 43 additions and 0 deletions
|
|
@ -24,6 +24,12 @@ class JsonController extends \Controller
|
|||
protected function throttle(string $key, int $max, int $window): bool
|
||||
{
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
|
||||
// Trusted server-local / whitelisted callers are never throttled: the static-site build
|
||||
// fetches the read API from the box itself thousands of times per build. The throttle is
|
||||
// for public abuse (real client IPs), not our own build. See Functions::isTrustedIp.
|
||||
if (\Functions::isTrustedIp($ip)) {
|
||||
return false;
|
||||
}
|
||||
$count = (int) \Db::getValue(
|
||||
"SELECT COUNT(*) FROM `api_requests`
|
||||
WHERE `requesting_ip` = ? AND `request` = ?
|
||||
|
|
|
|||
|
|
@ -186,6 +186,31 @@ class Functions {
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* True for trusted, server-local / whitelisted callers that should bypass public rate
|
||||
* limits (e.g. the SSG build hammering the read API from the box itself).
|
||||
* - loopback (127.0.0.1, ::1)
|
||||
* - the server's own address (same-host request)
|
||||
* - anything in the optional TRUSTED_IPS config list (comma-separated IPs or CIDRs —
|
||||
* e.g. the server's public IP, an internal build host, or a private range like 10.0.0.0/8)
|
||||
* Deliberately does NOT blanket-trust all private ranges: behind a private-IP reverse proxy
|
||||
* that would exempt every visitor. Add specific ranges to TRUSTED_IPS if your setup wants them.
|
||||
*/
|
||||
public static function isTrustedIp($ip) {
|
||||
if ($ip === '127.0.0.1' || $ip === '::1') return true;
|
||||
$serverAddr = $_SERVER['SERVER_ADDR'] ?? '';
|
||||
if ($serverAddr !== '' && $ip === $serverAddr) return true;
|
||||
if (defined('TRUSTED_IPS') && TRUSTED_IPS) {
|
||||
foreach (array_filter(array_map('trim', explode(',', TRUSTED_IPS))) as $entry) {
|
||||
if ($ip === $entry) return true;
|
||||
if (strpos($entry, '/') !== false
|
||||
&& filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)
|
||||
&& self::ip_in_range($ip, $entry)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function ip_in_range($ip, $range) {
|
||||
if (strpos($range, '/') == false)
|
||||
$range .= '/32';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,16 @@
|
|||
<?php
|
||||
// Harden the session cookie before it is created (login state rides on it).
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
$https = (($_SERVER['HTTPS'] ?? '') !== '' && $_SERVER['HTTPS'] !== 'off')
|
||||
|| ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https';
|
||||
session_set_cookie_params([
|
||||
'lifetime' => 0,
|
||||
'path' => '/',
|
||||
'httponly' => true,
|
||||
'secure' => $https,
|
||||
'samesite' => 'Lax',
|
||||
]);
|
||||
}
|
||||
@session_start();
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
require dirname(__DIR__) . '/config.php';
|
||||
|
|
|
|||
Loading…
Reference in a new issue