Proteção de força bruta em PHP

Se você tem um sistema onde os usuários podem se inscrever e escolher suas próprias senhas, provavelmente você é um alvo para ataques de força bruta, como um ataque de dicionário . Você pode limitar esse problema mostrando ao usuário o quão forte é sua senha. No entanto, forçar os usuários a inserir uma senha realmente forte irá irritá-los, pois eles gostam de algo que possam lembrar.

Outra forma que você pode colocar é bloquear um endereço IP por alguns minutos após uma série de falhas de login. Esta não é uma proteção à prova d’água, mas o hacker agora requer um botnet para realizar o ataque de força bruta. Este é um obstáculo que deve manter os hackers casuais e os script kiddies de fora. Portanto, com base nos dados que você está protegendo, essa deve ser uma defesa decente.

Configurar essa defesa não é difícil. Vou mostrar um exemplo de como fazer isso com o cache do usuário APC .

<?php
$apc_key
= "{$_SERVER['SERVER_NAME']}~login:{$_SERVER['REMOTE_ADDR']}";
$tries
= (int)apc_fetch($apc_key);
if ($tries >= 10) {
header
("HTTP/1.1 429 Too Many Requests");
echo
"You've exceeded the number of login attempts. We've blocked IP address {$_SERVER['REMOTE_ADDR']} for a few minutes.";
exit();
}

$success
= login($_POST['username'], $_POST['password']);
if (!$success) {
apcu_inc
($apc_key, $tries+1, 600); # store tries for 10 minutes
} else {
apc_delete
($apc_key);
}

Aumentando o tempo de bloqueio

O bloqueio de um IP pode afetar todo o escritório, o que pode ser irritante. Começar com um tempo limite baixo e aumentá-lo cada vez que um IP é bloqueado ajudará contra isso.

<?php
$apc_key
= "{$_SERVER['SERVER_NAME']}~login:{$_SERVER['REMOTE_ADDR']}";
$apc_blocked_key
= "{$_SERVER['SERVER_NAME']}~login-blocked:{$_SERVER['REMOTE_ADDR']}";

$tries
= (int)apc_fetch($apc_key);
if ($tries >= 10) {
header
("HTTP/1.1 429 Too Many Requests");
echo
"You've exceeded the number of login attempts. We've blocked IP address {$_SERVER['REMOTE_ADDR']} for a few minutes.";
exit();
}

$success
= login($_POST['username'], $_POST['password']);
if (!$success) {
$blocked
= (int)apc_fetch($apc_blocked_key);

apc_store
($apc_key, $tries+1, pow(2, $blocked+1)*60); # store tries for 2^(x+1) minutes: 2, 4, 8, 16, ...
apc_store
($apc_blocked_key, $blocked+1, 86400); # store number of times blocked for 24 hours
} else {
apc_delete
($apc_key);
apc_delete
($apc_blocked_key);
}