Implementar CAPTCHA no Formulário de Login Yii2

Advertisement

Ataques de força bruta para adivinhar senhas ainda são uma ameaça real à segurança. Este tutorial ensina a proteger o formulário de login no framework Yii2 ativando a validação CAPTCHA automaticamente após várias tentativas falhas, adicionando uma camada robusta de defesa.

Advertisement

Por que o CAPTCHA é Crucial para o Login?

Os ataques de força bruta são um método antigo, mas ainda muito usado. Hackers utilizam bots para testar automaticamente milhares de combinações de nome de usuário e senha. Um formulário desprotegido pode levar à invasão de contas administrativas.

Implementar CAPTCHA de forma condicional (apenas quando necessário) mantém uma boa experiência para o usuário legítimo e cria uma barreira para bots.

Passo 1: Modificar o Modelo LoginForm.php

Primeiro, adicione uma propriedade para o CAPTCHA e sua lógica de validação ao modelo `LoginForm`. A chave é tornar o CAPTCHA obrigatório apenas após um certo número de tentativas falhas.

Advertisement
<?php

namespace app\models;

use Yii;
use yii\base\Model;

class LoginForm extends Model
{
    public $username;
    public $password;
    public $rememberMe = true;
    public $verifyCode; // Nova propriedade para o CAPTCHA

    // ... outro código existente ...

    public function rules()
    {
        return [
            // Regras para usuário e senha
            [['username', 'password'], 'required'],
            ['rememberMe', 'boolean'],
            ['password', 'validatePassword'],

            // REGRAS CAPTCHA: Obrigatório apenas se loginFailed for true
            ['verifyCode', 'required', 'when' => function($model) {
                return $model->loginFailed;
            }, 'message' => 'O código CAPTCHA é obrigatório após várias falhas.'],
            ['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 PRINCIPAL: Incrementar o contador de falhas
                $attempts = Yii::$app->session->get('_loginAttempts', 0) + 1;
                Yii::$app->session->set('_loginAttempts', $attempts);

                $this->addError($attribute, 'Nome de usuário ou senha incorretos.');
            } else {
                // Zerar o contador no sucesso
                Yii::$app->session->remove('_loginAttempts');
            }
        }
    }

    // Getter para verificar o status de falha
    public function getLoginFailed()
    {
        // CAPTCHA ativa após 3 ou mais falhas
        return Yii::$app->session->get('_loginAttempts', 0) >= 3;
    }

    // ... método getUser() e outros ...
}

Explicação do Código:

  • A propriedade $verifyCode armazena a entrada do CAPTCHA.
  • O método getLoginFailed() verifica a variável de sessão _loginAttempts. Retorna true se as falhas forem ≥ 3.
  • O parâmetro 'when' garante que as regras do verifyCode só se apliquem se loginFailed for true.

Passo 2: Exibir o CAPTCHA na View Login.php

Em seguida, modifique o arquivo da view de login para renderizar o widget CAPTCHA somente quando a condição $model->loginFailed for atendida.

Advertisement
<?php

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

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

$this->title = 'Login';
?>
<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() ?>

                <!-- SEÇÃO CONDICIONAL DO CAPTCHA -->
                <?php if ($model->loginFailed): ?>
                    <div class="alert alert-warning">
                        Muitas tentativas de login falhas. Verifique que você é humano.
                    </div>
                    <?= $form->field($model, 'verifyCode')->widget(\yii\captcha\Captcha::className()) ?>
                <?php endif; ?>

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

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

Últimos artigos