InicioFramework PHPProtege Login Yii2 de Ataques con CAPTCHA

Protege Login Yii2 de Ataques con CAPTCHA

Advertisement

Los ataques de fuerza bruta para adivinar contraseñas siguen siendo una amenaza real para la seguridad web. Esta guía explica paso a paso cómo proteger el formulario de inicio de sesión en el framework Yii2 activando la validación CAPTCHA automáticamente tras varios intentos fallidos, añadiendo así una capa de defensa robusta a tu aplicación.

Advertisement

¿Por qué es Crucial el CAPTCHA para el Login?

Los ataques de fuerza bruta son un método antiguo pero persistentemente eficaz. Los atacantes usan bots para probar automáticamente miles de combinaciones de usuario y contraseña. Un formulario de login sin protección puede llevar al robo de cuentas de administrador y datos sensibles.

  • Bloquea Bots: El CAPTCHA (Prueba de Turing completamente automática y pública para diferenciar ordenadores de humanos) está diseñado para distinguir humanos de robots.
  • Aumenta la Dificultad: Activar un CAPTCHA tras varios intentos fallidos aumenta significativamente el esfuerzo requerido por el atacante.
  • Estrategia de Defensa en Profundidad: El CAPTCHA es una capa de seguridad adicional vital, que complementa las contraseñas fuertes y la limitación de intentos.

Implementar CAPTCHA de forma condicional (solo cuando es necesario) mantiene una buena experiencia de usuario para los legítimos, a la vez que crea una barrera para bots y atacantes.

Paso 1: Modificar el Modelo LoginForm.php

Primero, añade una propiedad para el CAPTCHA y su lógica de validación al modelo `LoginForm`. La clave es hacer que el CAPTCHA sea obligatorio solo tras un número determinado de intentos fallidos.

Advertisement
<?php

namespace app\models;

use Yii;
use yii\base\Model;

class LoginForm extends Model
{
    public $username;
    public $password;
    public $rememberMe = true;
    public $verifyCode; // Nueva propiedad para el CAPTCHA

    // ... otro código existente ...

    public function rules()
    {
        return [
            // Reglas para usuario y contraseña
            [['username', 'password'], 'required'],
            ['rememberMe', 'boolean'],
            ['password', 'validatePassword'],

            // REGLAS CAPTCHA: Obligatorio solo si loginFailed es true
            ['verifyCode', 'required', 'when' => function($model) {
                return $model->loginFailed;
            }, 'message' => 'El código CAPTCHA es obligatorio tras varios intentos fallidos.'],
            ['verifyCode', 'captcha', 'when' => function($model) {
                return $model->loginFailed;
            }],
        ];
    }

    public function validatePassword($attribute, $params)
    {
        if (!$this->hasErrors()) {
            $user = $this->getUser();
            if (!$user || !$user->validatePassword($this->password)) {
                // LÓGICA CLAVE: Incrementar el contador de intentos fallidos
                $attempts = Yii::$app->session->get('_loginAttempts', 0) + 1;
                Yii::$app->session->set('_loginAttempts', $attempts);

                $this->addError($attribute, 'Usuario o contraseña incorrectos.');
            } else {
                // Reiniciar contador si el login es exitoso
                Yii::$app->session->remove('_loginAttempts');
            }
        }
    }

    // Getter para verificar el estado de fallo
    public function getLoginFailed()
    {
        // El CAPTCHA se activa tras 3 o más fallos
        return Yii::$app->session->get('_loginAttempts', 0) >= 3;
    }

    // ... método getUser() y otros ...
}

Explicación del Código Clave:

  • La propiedad $verifyCode almacena la entrada del CAPTCHA del usuario.
  • El método getLoginFailed() verifica la variable de sesión _loginAttempts. Devuelve true si los intentos fallidos son ≥ 3.
  • El parámetro 'when' en las reglas de validación asegura que las reglas para verifyCode solo se apliquen cuando loginFailed sea true.
  • El contador de intentos fallidos en la sesión se incrementa con cada fallo en la validación de la contraseña. Un inicio de sesión exitoso lo reinicia.

Paso 2: Mostrar CAPTCHA en la Vista Login.php

A continuación, modifica el archivo de la vista de login para renderizar el widget CAPTCHA solo cuando se cumpla la condición $model->loginFailed.

Advertisement
<?php

use yii\helpers\Html;
use yii\bootstrap5\ActiveForm; // o yii\widgets\ActiveForm

/* @var $this yii\web\View */
/* @var $form yii\widgets\ActiveForm */
/* @var $model app\models\LoginForm */

$this->title = 'Iniciar Sesión';
?>
<div class="site-login">
    <h1><?= Html::encode($this->title) ?></h1>

    <div class="row">
        <div class="col-lg-5">
            <?php $form = ActiveForm::begin(['id' => 'login-form']); ?>

                <?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?>

                <?= $form->field($model, 'password')->passwordInput() ?>

                <?= $form->field($model, 'rememberMe')->checkbox() ?>

                <!-- SECCIÓN CONDICIONAL DEL CAPTCHA -->
                <?php if ($model->loginFailed): ?>
                    <div class="alert alert-warning">
                        Demasiados intentos de inicio de sesión fallidos. Por favor, verifica que eres humano.
                    </div>
                    <?= $form->field($model, 'verifyCode')->widget(\yii\captcha\Captcha::className()) ?>
                <?php endif; ?>

                <div class="form-group">
                    <?= Html::submitButton('Iniciar Sesión', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
                </div>

            <?php ActiveForm::end(); ?>
        </div>
    </div>
</div>

Paso 3: Configurar la Acción CAPTCHA en el Controlador

El widget CAPTCHA requiere una acción dedicada en el controlador para generar la imagen y validar el código. Asegúrate de que la acción ‘captcha’ esté configurada en `SiteController`.

<?php

namespace app\controllers;

use yii\web\Controller;

class SiteController extends Controller
{
    // ... otras actions() ...

    public function actions()
    {
        return [
            'error' => ['class' => 'yii\web\ErrorAction'],
            // LA ACCIÓN CAPTCHA DEBE ESTAR PRESENTE
            'captcha' => [
                'class' => 'yii\captcha\CaptchaAction',
                'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null, // Importante para testing
                'minLength' => 4, // Longitud mínima del código
                'maxLength' => 5, // Longitud máxima del código
                'offset' => 2,    // Dificultad visual
                'testLimit' => 1, // Cuántas veces se puede usar un código (1 = un solo uso)
            ],
            // ... otras acciones ...
        ];
    }

    // ... actionLogin() y otros métodos ...
}

Parámetros como minLength, maxLength y testLimit te permiten ajustar la seguridad y usabilidad del CAPTCHA para tu aplicación .

Notas Importantes y Seguridad Adicional

  • Basado en Sesión: Esta implementación depende de las sesiones de PHP. Asegúrate de que la configuración de sesión de tu servidor sea segura.
  • No es la Única Solución: El CAPTCHA es una excelente capa defensiva, pero debes combinarla con otras medidas como:
    1. Limitación de Tasa de Login: Usa componentes como `yiifiltersRateLimiter` para restringir intentos de login desde una misma IP en un período .
    2. Contraseñas Fuertes: Exige contraseñas complejas para todos los usuarios, especialmente administradores.
    3. Registro y Monitorización: Registra todos los intentos de inicio de sesión, tanto exitosos como fallidos, para su análisis.
  • Reinicio del Contador: En el código anterior, el contador se reinicia con un login exitoso. También podrías implementar un reinicio basado en tiempo (ej., tras 30 minutos) para mejorar la experiencia de usuario.

Al implementar este CAPTCHA condicional, añades una capa de seguridad vital que interrumpe inteligentemente el flujo automatizado de los ataques de fuerza bruta, manteniendo la facilidad de uso para usuarios legítimos.

Artículos Recientes