domingo, 10 de agosto de 2025

Cómo Crear una Página 403 Personalizada al Estilo Netflix con Contador de Intentos y Baneo por IP

 ¿Alguna vez has querido darle un toque único a los errores de tu sitio web? Un error 403, que indica que el acceso está prohibido, no tiene por qué ser aburrido. En este artículo, te mostraré cómo crear una página 403.php personalizada con un diseño elegante, similar al de Netflix, que no solo informa al usuario, sino que también lleva un registro de los intentos de acceso fallidos y banea automáticamente a los intrusos persistentes. ¡Vamos a ello! 💻



Paso 1: Configurar el Servidor Web

Para que tu servidor use esta página personalizada, primero necesitas configurar el archivo de configuración de tu servidor web (por ejemplo, Apache o Nginx).

Para Apache (.htaccess)

Si usas Apache, puedes simplemente agregar esta línea a tu archivo .htaccess en la raíz de tu sitio:

ErrorDocument 403 /403.php

Esto le dice a Apache que, cuando ocurra un error 403, debe mostrar el contenido de nuestro archivo 403.php.

Paso 2: Crear el Archivo 403.php

Este es el corazón de nuestro proyecto. El archivo 403.php manejará tanto el diseño visual como la lógica del baneo. Crearemos un archivo en la raíz de tu sitio con este nombre.

El Código

Aquí está el código completo que debes copiar y pegar en tu archivo 403.php. A continuación, desglosaremos las partes más importantes.

PHP
<?php
// Configuración
$log_file = 'access_log.txt';
$max_attempts = 5;
$ban_duration = 86400; // 24 horas en segundos

// Obtener la dirección IP del visitante
$ip_address = $_SERVER['REMOTE_ADDR'];

// Si el archivo de registro no existe, créalo
if (!file_exists($log_file)) {
    file_put_contents($log_file, '');
}

// Leer el archivo de registro y deserializar los datos
$log_data = unserialize(file_get_contents($log_file));

// Inicializar el array de datos si no existe
if (!is_array($log_data)) {
    $log_data = [];
}

// Si la IP no está en el registro, inicializarla
if (!isset($log_data[$ip_address])) {
    $log_data[$ip_address] = [
        'attempts' => 0,
        'timestamp' => time()
    ];
}

// Incrementar el contador de intentos y actualizar el timestamp
$log_data[$ip_address]['attempts']++;
$log_data[$ip_address]['timestamp'] = time();

// Escribir los datos actualizados de vuelta al archivo
file_put_contents($log_file, serialize($log_data));

// Verificar si se ha excedido el número de intentos
if ($log_data[$ip_address]['attempts'] > $max_attempts) {
    // Si la IP está baneada, ejecutar el comando de iptables
    $command = "sudo /sbin/iptables -A INPUT -s $ip_address -j DROP";
    exec($command);
    $is_banned = true;
} else {
    $is_banned = false;
}

// Si la IP ya ha excedido los intentos y han pasado 24 horas, resetear
if ($log_data[$ip_address]['attempts'] > $max_attempts && (time() - $log_data[$ip_address]['timestamp']) > $ban_duration) {
    $log_data[$ip_address]['attempts'] = 0;
    file_put_contents($log_file, serialize($log_data));
}

// Definir el estado del error 403
header("HTTP/1.1 403 Forbidden");
?>
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Error 403 - Acceso Denegado</title>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Netflix+Sans:wght@400;700&display=swap');

        :root {
            --netflix-red: #e50914;
            --dark-gray: #141414;
            --text-color: #f1f1f1;
        }

        body {
            background-color: var(--dark-gray);
            color: var(--text-color);
            font-family: 'Netflix Sans', sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            text-align: center;
        }

        .container {
            max-width: 600px;
            padding: 2rem;
            border-radius: 10px;
            background-color: rgba(0, 0, 0, 0.5);
            backdrop-filter: blur(10px);
        }

        .logo {
            color: var(--netflix-red);
            font-size: 4rem;
            font-weight: 700;
            margin-bottom: 1rem;
            text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5);
        }

        h1 {
            font-size: 2.5rem;
            margin-bottom: 0.5rem;
        }

        p {
            font-size: 1.2rem;
            line-height: 1.5;
        }

        .counter {
            margin-top: 1rem;
            font-size: 1rem;
            color: rgba(255, 255, 255, 0.7);
        }

        .highlight {
            color: var(--netflix-red);
            font-weight: bold;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="logo">N</div>
        <h1>Error 403 - Acceso Denegado</h1>
        <p>Parece que no tienes permiso para acceder a esta página. 😔</p>
        <p>
            <?php if ($is_banned): ?>
                Tu dirección IP ha sido <span class="highlight">baneada</span> temporalmente por exceder el número de intentos. Por favor, intenta de nuevo más tarde.
            <?php else: ?>
                Intento actual: <span class="highlight"><?php echo $log_data[$ip_address]['attempts']; ?></span> de <span class="highlight"><?php echo $max_attempts; ?></span>.
            <?php endif; ?>
        </p>
    </div>
</body>
</html>

Desglose del Código

Parte de PHP (Lógica del Contador y Baneo)

  • Configuración: Definimos el archivo de registro (access_log.txt), el número máximo de intentos ($max_attempts) y la duración del baneo ($ban_duration).

  • Registro de IP: Obtenemos la IP del visitante y la usamos como clave en un array para llevar el control de los intentos.

  • unserialize() y serialize(): Estos son cruciales. Nos permiten guardar un array de datos de PHP en un archivo de texto plano de manera eficiente, y luego recuperarlo. El archivo access_log.txt almacenará un registro de todas las IPs que han accedido a la página de error.

  • El Baneo con iptables: Cuando la IP supera el límite de intentos, usamos exec() para ejecutar un comando de iptables. Este comando (sudo /sbin/iptables -A INPUT -s $ip_address -j DROP) le dice al firewall del sistema operativo que descarte todo el tráfico entrante desde esa dirección IP. ¡Importante! El servidor web debe tener permisos para ejecutar este comando, lo cual a menudo requiere configuración de sudoers. Esto puede ser un riesgo de seguridad si no se configura correctamente. Asegúrate de entender lo que estás haciendo o consulta con un administrador de sistemas.

  • Reseteo del Contador: También hemos añadido una lógica para resetear el contador de intentos si ha pasado el tiempo de baneo, para que la IP pueda volver a intentarlo después de 24 horas.

  • Header 403: Finalmente, establecemos el encabezado HTTP/1.1 403 Forbidden para asegurarnos de que los navegadores y los motores de búsqueda entiendan que se trata de un error de acceso denegado.

Parte de HTML y CSS (Diseño)

  • Estructura HTML: Un diseño simple con un div.container que centra el contenido.

  • Estilos CSS3: Usamos un CSS minimalista para lograr ese look de Netflix.

    • --netflix-red: Una variable CSS para el color rojo característico de Netflix.

    • font-family: 'Netflix Sans': Importamos una fuente similar a la utilizada por Netflix para mantener la coherencia estética.

    • backdrop-filter: blur(10px): Este es un efecto moderno que le da un toque translúcido y borroso al fondo, similar a lo que vemos en muchas interfaces de usuario actuales.

Paso 3: Subir los Archivos

Sube el archivo 403.php y el archivo .htaccess (si lo modificaste) a la raíz de tu sitio web. Asegúrate de que los permisos del archivo access_log.txt permitan al servidor web escribir en él (por ejemplo, chmod 666).

¡Y listo! Ahora, cuando un usuario intente acceder a una página restringida en tu sitio, verá una elegante página de error 403 que además protege tu sitio de intentos de acceso repetidos.

Consideraciones de Seguridad y Mejoras

  • Permisos de iptables: La parte de iptables puede ser compleja. Si no tienes experiencia, te sugiero que primero pruebes la página sin esa funcionalidad para evitar bloquear tu propio servidor. Una alternativa menos agresiva sería simplemente registrar la IP y enviar una notificación.

  • Seguridad del archivo de registro: El archivo access_log.txt es sensible. Asegúrate de que no sea accesible públicamente, por ejemplo, moviéndolo fuera del directorio raíz del servidor o protegiéndolo con .htaccess.

  • Limpieza del registro: Con el tiempo, el archivo de registro puede crecer mucho. Podrías agregar una tarea cron para limpiar las entradas antiguas o una lógica que lo haga automáticamente.

No hay comentarios:

Publicar un comentario