/home/suroeste/public_html/payments.transportessuroeste.com/src/Middleware
Edit: /home/suroeste/public_html/payments.transportessuroeste.com/src/Middleware/RateLimiter.php (6723B)
db = Database::getInstance();
$this->maxRequests = SECURITY_CONFIG['rate_limit_requests'] ?? 100;
$this->windowSeconds = SECURITY_CONFIG['rate_limit_window'] ?? 60;
$this->banTime = SECURITY_CONFIG['rate_limit_ban_time'] ?? 3600;
}
/**
* Verificar y aplicar rate limiting
*/
public function check(string $identifier, string $endpoint = '/'): void
{
try {
if ($this->isBlocked($identifier)) {
LogService::security('rate_limit_exceeded', "IP bloqueada: {$identifier}", [
'identifier' => $identifier,
'endpoint' => $endpoint
]);
throw new RateLimitException('Demasiadas peticiones', $this->banTime);
}
$count = $this->incrementCounter($identifier, $endpoint);
if ($count > $this->maxRequests) {
$this->blockIdentifier($identifier);
LogService::security('rate_limit_exceeded', "Limite excedido, bloqueando: {$identifier}", [
'requests' => $count,
'limit' => $this->maxRequests
]);
throw new RateLimitException('Limite de peticiones excedido', $this->banTime);
}
} catch (RateLimitException $e) {
throw $e;
} catch (\Exception $e) {
LogService::error('rate_limiter', 'Error en rate limiter: ' . $e->getMessage());
}
}
/**
* Verificar si IP esta bloqueada
*/
private function isBlocked(string $identifier): bool
{
try {
$sql = "SELECT id FROM blocked_ips WHERE ip_address = ? AND (is_permanent = 1 OR expires_at > NOW())";
$stmt = $this->db->getConnection()->prepare($sql);
$stmt->execute([$identifier]);
return $stmt->fetch() !== false;
} catch (\Exception $e) {
return false;
}
}
/**
* Incrementar contador de peticiones
*/
private function incrementCounter(string $identifier, string $endpoint): int
{
$pdo = $this->db->getConnection();
$now = date('Y-m-d H:i:s');
$windowStart = date('Y-m-d H:i:s', time() - $this->windowSeconds);
$windowEnd = date('Y-m-d H:i:s', time() + $this->windowSeconds);
try {
$stmt = $pdo->prepare("DELETE FROM rate_limit_tracking WHERE identifier = ? AND window_end < ?");
$stmt->execute([$identifier, $windowStart]);
$stmt = $pdo->prepare(
"SELECT id, request_count FROM rate_limit_tracking
WHERE identifier = ? AND identifier_type = 'ip' AND endpoint = ?
AND window_start <= ? AND window_end >= ?"
);
$stmt->execute([$identifier, $endpoint, $now, $now]);
$existing = $stmt->fetch(\PDO::FETCH_ASSOC);
if ($existing) {
$newCount = (int)$existing['request_count'] + 1;
$stmt = $pdo->prepare("UPDATE rate_limit_tracking SET request_count = ? WHERE id = ?");
$stmt->execute([$newCount, $existing['id']]);
return $newCount;
}
$stmt = $pdo->prepare(
"INSERT INTO rate_limit_tracking (identifier, identifier_type, endpoint, request_count, window_start, window_end)
VALUES (?, 'ip', ?, 1, ?, ?)"
);
$stmt->execute([$identifier, $endpoint, $now, $windowEnd]);
return 1;
} catch (\Exception $e) {
LogService::error('rate_limiter', 'Error MySQL incrementando contador: ' . $e->getMessage());
return 1;
}
}
/**
* Bloquear IP
*/
private function blockIdentifier(string $identifier): void
{
$pdo = $this->db->getConnection();
$expiresAt = date('Y-m-d H:i:s', time() + $this->banTime);
try {
$stmt = $pdo->prepare("SELECT id FROM blocked_ips WHERE ip_address = ?");
$stmt->execute([$identifier]);
$existing = $stmt->fetch();
if ($existing) {
$stmt = $pdo->prepare("UPDATE blocked_ips SET expires_at = ?, reason = 'Rate limit exceeded' WHERE ip_address = ?");
$stmt->execute([$expiresAt, $identifier]);
} else {
$stmt = $pdo->prepare(
"INSERT INTO blocked_ips (ip_address, reason, blocked_by, is_permanent, expires_at)
VALUES (?, 'Rate limit exceeded', 'system', 0, ?)"
);
$stmt->execute([$identifier, $expiresAt]);
}
} catch (\Exception $e) {
LogService::error('rate_limiter', 'Error bloqueando IP en MySQL: ' . $e->getMessage());
}
}
/**
* Obtener headers de rate limit para respuesta
*/
public function getHeaders(string $identifier): array
{
try {
$pdo = $this->db->getConnection();
$stmt = $pdo->prepare(
"SELECT request_count, window_end FROM rate_limit_tracking
WHERE identifier = ? ORDER BY window_end DESC LIMIT 1"
);
$stmt->execute([$identifier]);
$result = $stmt->fetch(\PDO::FETCH_ASSOC);
$remaining = max(0, $this->maxRequests - ($result['request_count'] ?? 0));
$reset = $result ? strtotime($result['window_end']) : time() + $this->windowSeconds;
return [
'X-RateLimit-Limit' => $this->maxRequests,
'X-RateLimit-Remaining' => $remaining,
'X-RateLimit-Reset' => $reset
];
} catch (\Exception $e) {
return [
'X-RateLimit-Limit' => $this->maxRequests,
'X-RateLimit-Remaining' => $this->maxRequests,
'X-RateLimit-Reset' => time() + $this->windowSeconds
];
}
}
}