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.
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.
- Bloqueia Bots: O CAPTCHA (“Teste de Turing público completamente automatizado para diferenciar computadores de humanos”) é feito para distinguir pessoas de máquinas.
- Aumenta a Dificuldade: Ativar um CAPTCHA após várias falhas aumenta muito o esforço necessário para o invasor.
- Estratégia de Defesa: O CAPTCHA é uma camada adicional de segurança essencial.
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.
<?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
$verifyCodearmazena a entrada do CAPTCHA. - O método
getLoginFailed()verifica a variável de sessão_loginAttempts. Retornatruese as falhas forem ≥ 3. - O parâmetro
'when'garante que as regras doverifyCodesó se apliquem seloginFailedfortrue.
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.
<?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>Passo 3: Configurar a Ação CAPTCHA no Controller
O widget CAPTCHA requer uma action dedicada no controller. Certifique-se de que a action ‘captcha’ está configurada no `SiteController`.
<?php
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
// ... outras actions() ...
public function actions()
{
return [
'error' => ['class' => 'yii\web\ErrorAction'],
// A ACTION CAPTCHA DEVE EXISTIR
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
'minLength' => 4,
'maxLength' => 5,
'offset' => 2,
'testLimit' => 1,
],
// ... outras actions ...
];
}
// ... actionLogin() e outros métodos ...
}Notas Importantes & Segurança Adicional
- Baseado em Sessão: Esta implementação usa sessões PHP. Configure-as de forma segura.
- Não é a Única Solução: Combine o CAPTCHA com outras medidas:
- Limitação de Taxa (Rate Limiting): Use componentes como `yii\filters\RateLimiter`.
- Senhas Fortes: Exija senhas complexas.
- Logs: Registre todas as tentativas de login.
- Redefinir Contador: O contador zera no login bem-sucedido. Uma redefinição por tempo também é viável.
Ao implementar este CAPTCHA condicional, você adiciona uma camada de segurança vital que interrompe o fluxo automatizado de ataques de força bruta, mantendo a usabilidade para usuários legítimos.


