feat(auth): throttle de login esgotado alimenta o IP ban da frota

Cada vez que o throttle local bloqueia (5/min ou 30/h por IP, 10/50
por conta) chama platform_ip_record_offence() — já carregado pelo
platform_connect() de config/db.php. A frota lembra-se entre sessões
de throttle onde o throttle sozinho esquece ao fim de uma hora: 3
vezes em 24h = 1h de ban, 5 = 1 dia, 8 = 1 semana, 12 = permanente
(IP_BAN_LADDER, já em vigor desde 25/09 para todo o site).

Also: o aviso à frota de login falhado (functions_security_events.php)
já funciona — o comentário que dizia "não faz nada até ao refresh do
vendorizado" ficou desatualizado com o submódulo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
andreandClaude Sonnet 5 committed 2026-09-25 12:48:17 +01:00
1 parent 0ea8eb1cde
commit ee30e6f116
1 file changed
+8 -3
+8 -3
View File
@@ -20,9 +20,7 @@ if (!empty($_SESSION['user_id'])) {
} }
// O login é deste site, não o partilhado, por isso é daqui que a frota fica a // O login é deste site, não o partilhado, por isso é daqui que a frota fica a
// saber das falhas (platform-tools/functions_security_events.php). A cópia // saber das falhas (platform-tools/functions_security_events.php).
// vendorizada ainda não traz esse ficheiro (ver VENDORED.md); até ao próximo
// refresh isto não faz nada.
$report_login = function (string $kind) use ($pdo): void { $report_login = function (string $kind) use ($pdo): void {
$f = dirname(__DIR__, 2) . '/shared/platform-tools/functions_security_events.php'; $f = dirname(__DIR__, 2) . '/shared/platform-tools/functions_security_events.php';
if (!is_file($f)) return; if (!is_file($f)) return;
@@ -44,6 +42,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!throttle_check($pdo, 'login') || !throttle_check($pdo, 'login_acct', 10, 50, $acct)) { if (!throttle_check($pdo, 'login') || !throttle_check($pdo, 'login_acct', 10, 50, $acct)) {
$report_login('login_throttled'); $report_login('login_throttled');
audit('auth.login_throttled', ['target_type' => 'user', 'target_label' => audit_mask_email((string) ($_POST['email'] ?? '')), 'actor' => null]); audit('auth.login_throttled', ['target_type' => 'user', 'target_label' => audit_mask_email((string) ($_POST['email'] ?? '')), 'actor' => null]);
// O throttle por si só esquece em uma hora; isto é a memória que falta —
// um IP que esgota o throttle repetidamente ao longo de dias acaba banido
// pela frota (functions_ip_ban.php, o mesmo ip_bans que já bloqueia à
// porta desde 25/09). 3 vezes em 24h = 1h de ban; 12 = permanente.
if (function_exists('platform_ip_record_offence')) {
platform_ip_record_offence($pdo, 'login_throttled');
}
throttle_block('login form'); throttle_block('login form');
} }
throttle_record($pdo, 'login'); throttle_record($pdo, 'login');