How jekcms tracks failed login attempts and automatically blocks abusive IPs to stop brute force attacks: no Redis, memcached, or external dependency required.
Why This Matters
Credential-stuffing tools can fire hundreds of login attempts per minute against any publicly accessible endpoint. An attacker with a leaked username/password list can automate POST requests to /admin/login.php faster than any human could type. Without protection, that's a direct line to your database taking a beating from authentication queries, and every attempt is a shot at guessing correctly.
jekcms handles this at the login layer, not as an optional plugin.
How jekcms Actually Tracks Login Attempts
Every login attempt (successful or not) is recorded in a login_attempts table. When failures from a given IP cross a threshold, SecurityHelper::blockIp() adds that IP to a blocked_ips table, and subsequent requests from that IP are rejected before they ever reach the password check. This is a database-backed mechanism, not a file-based rate limiter with per-key cache files, which matters in practice because it works identically If you're on shared hosting or a multi-server setup, without needing a shared filesystem or an external cache like Redis.
// admin/login.php (simplified)
db()->insert('login_attempts', [
'ip' => $ip,
'username'=> $username,
'success' => $success ? 1 : 0,
]);
if (!$success) {
$recentFailures = db()->fetch(
"SELECT COUNT(*) as c FROM login_attempts
WHERE ip = ? AND success = 0 AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)",
[$ip]
)['c'];
if ($recentFailures >= $threshold) {
SecurityHelper::blockIp($ip);
}
}
Once an IP lands in blocked_ips, it stays blocked until it's manually cleared or the block expires, depending on how the installation is configured: there's no per-second sliding window to reason about, just a straightforward attempt count over a rolling period.
What jekcms Doesn't Do (and Why That's Fine)
There's no separate, general-purpose rate-limiting class covering arbitrary actions with configurable per-action limits and file-based counters. The login-attempt protection above is specific to authentication. For the REST API the equivalent is simpler: an hourly request cap per client IP (100 by default, changed with API_RATE_LIMIT). It is not a sliding-window limiter reused across every subsystem. If your installation needs broader rate limiting for a custom endpoint you're building, that's something you'd add yourself; jekcms's core focuses its built-in protection where it matters most: the login form.
Practical Impact
The practical effect for a site owner: an attacker running a credential-stuffing list against your login form runs out of attempts from a given IP well before they'd get through any meaningful fraction of a password list, and legitimate users who mistype their password a few times in a row are never affected by thresholds set for that kind of everyday behaviour.