antibot-laravel maintained by marcelocardozo
Antibot para Laravel
Sistema de detecção e bloqueio de bots, proxies e VPNs para Laravel.
Detecta automaticamente ferramentas de automação (Selenium, Puppeteer, Playwright, Cypress, PhantomJS, etc.), analisa fingerprints do navegador, verifica proxies/VPNs via ProxyCheck.io e bloqueia visitantes suspeitos com base em um sistema de pontuação configurável.
Requisitos
- PHP 8.2+
- Laravel 11, 12 ou 13
- Chave de API do ProxyCheck.io (gratuito)
Instalação
composer require marcelocardozo/antibot-laravel
Publicar configuração, migrations e assets:
php artisan vendor:publish --provider="MarceloCardozo\Antibot\AntibotServiceProvider"
Ou publicar separadamente:
# Apenas configuração
php artisan vendor:publish --tag=antibot-config
# Apenas migrations
php artisan vendor:publish --tag=antibot-migrations
# Apenas assets (JS)
php artisan vendor:publish --tag=antibot-assets
# Apenas views (templates)
php artisan vendor:publish --tag=antibot-views
Executar as migrations:
php artisan migrate
Configuração
Adicione ao .env:
ANTIBOT_PROXYCHECK_API_KEY=sua-chave-aqui
ANTIBOT_TEST_IP=192.145.220.95
| Variável | Descrição |
|---|---|
ANTIBOT_PROXYCHECK_API_KEY |
Chave de API do ProxyCheck.io |
ANTIBOT_TEST_IP |
IP usado em localhost para testes (substitui 127.0.0.1) |
O arquivo config/antibot.php contém todas as opções:
return [
'route_prefix' => 'antibot', // Prefixo das rotas
'route_middleware' => ['web'], // Middleware das rotas
'pagina_inicial' => '/', // Página com detecção completa
'redirect_url' => '', // Redirecionar após aprovação (vazio = não redireciona)
'block_redirect_url' => '/', // Redirecionar bloqueados (padrão: página inicial)
'tempo_minimo' => 5000, // Tempo mínimo de verificação (ms)
'tela_carregamento' => 'spinner', // Template: 'spinner' ou 'cloudflare'
'score_minimo' => 50, // Score mínimo para bloquear
'bloquear_bot' => true, // Bloquear bots detectados
'bloquear_proxy' => true, // Bloquear proxies
'bloquear_vpn' => true, // Bloquear VPNs
'paises_permitidos' => ['BR'], // Países permitidos (vazio = todos)
'regras' => [ ... ], // 40+ regras de detecção com pontuação
];
Uso
1. Página inicial (detecção completa)
Na Blade da página onde o visitante entra no site, adicione a diretiva @antibot:
<!DOCTYPE html>
<html>
<head></head>
<body>
@antibot
<h1>Meu site</h1>
</body>
</html>
Quando o visitante acessa a página:
- A página é ocultada e um loading é exibido
- 40+ verificações client-side são executadas
- APIs server-side analisam IP, proxy, VPN e User-Agent
- O resultado é salvo no banco de dados
- Se aprovado: a página é liberada
- Se bloqueado: redireciona para
block_redirect_urlou exibe 404
2. Páginas secundárias (verificação rápida)
Para páginas internas que só precisam verificar se o visitante já foi aprovado:
<body>
@antibotVerify
<h1>Página interna</h1>
</body>
3. Middleware (proteção server-side)
Para proteger rotas no server-side (bloqueia bots que pulam o JavaScript):
// routes/web.php
Route::middleware('antibot.verify')->group(function () {
Route::get('/area-protegida', [MeuController::class, 'index']);
Route::get('/checkout', [CheckoutController::class, 'index']);
});
O middleware verifica no banco se o IP do visitante já foi aprovado. Se não existir registro ou se estiver bloqueado, redireciona para block_redirect_url ou retorna 404.
4. Combinando proteções
Para proteção máxima, combine middleware + diretiva Blade:
// routes/web.php
Route::middleware('antibot.verify')->group(function () {
Route::get('/oferta', [OfertaController::class, 'index']);
});
{{-- resources/views/oferta/index.blade.php --}}
<body>
@antibot
<h1>Oferta exclusiva</h1>
</body>
Redirecionamento de bloqueados
Por padrão, visitantes bloqueados ou não verificados são redirecionados para / (página inicial). Para customizar:
// config/antibot.php
// Padrão: redireciona para a página inicial
'block_redirect_url' => '/',
// Path relativo (mesma aplicação)
'block_redirect_url' => '/acesso-negado',
// URL externa
'block_redirect_url' => 'https://google.com',
// Vazio = exibe o template 404 do pacote
'block_redirect_url' => '',
Funciona tanto no middleware (server-side) quanto no JavaScript (client-side).
Facade
O pacote disponibiliza uma Facade para uso programático:
use MarceloCardozo\Antibot\Facades\Antibot;
// Resolver IP real do visitante
$ip = Antibot::resolveIp($request);
// Verificar proxy/VPN
$data = Antibot::check($ip);
// Retorna: ['ip', 'asn', 'provider', 'isocode', 'city', 'proxy', 'vpn', ...]
// Detectar dispositivo
$device = Antibot::detect($request->userAgent());
// Retorna: ['bot', 'client_name', 'device_type', 'os_name', ...]
// Flags de servidor suspeitas
$flags = Antibot::getServerFlags($request);
// Retorna: ['tool_ua', 'short_ua', 'no_accept_language', ...]
Models
O pacote registra dois models Eloquent que permitem consultar os dados de detecção no seu código:
MarceloCardozo\Antibot\Models\Acesso— Registro de cada visitante analisadoMarceloCardozo\Antibot\Models\Navegacao— Páginas visitadas por cada acesso
Campos disponíveis
Acesso:
| Campo | Tipo | Descrição |
|---|---|---|
id |
bigint | ID auto-incremento |
data_hora |
timestamp | Data/hora do acesso |
ip |
string | IP do visitante |
url |
text | URL acessada |
asn |
string | ASN do provedor |
hostname |
string | Hostname do IP |
provider |
string | Provedor de internet |
organisation |
string | Organização |
isocode |
string | Código do país (BR, US, etc.) |
regioncode |
string | Código da região |
city |
string | Cidade |
proxy |
string | "yes" ou "no" |
vpn |
string | "yes" ou "no" |
bot |
string | "yes" ou "no" |
client_name |
string | Navegador (Chrome, Firefox, etc.) |
client_type |
string | Tipo do client (browser, app, etc.) |
client_version |
string | Versão do navegador |
device_brand |
string | Marca do dispositivo |
device_model |
string | Modelo do dispositivo |
device_type |
string | Tipo (desktop, smartphone, tablet) |
os_name |
string | Sistema operacional |
os_platform |
string | Plataforma |
os_version |
string | Versão do SO |
os_family |
string | Família do SO |
bloqueado |
string | "true" ou "false" |
motivo_bloqueio |
text | Motivo(s) do bloqueio |
created_at |
timestamp | Criado em |
updated_at |
timestamp | Atualizado em |
Navegacao:
| Campo | Tipo | Descrição |
|---|---|---|
id |
bigint | ID auto-incremento |
data_hora |
timestamp | Data/hora da navegação |
ip |
string | IP do visitante |
url |
text | URL visitada |
referrer |
text | Página de origem |
acesso_id |
bigint | FK para antibot_acessos |
created_at |
timestamp | Criado em |
updated_at |
timestamp | Atualizado em |
Exemplo em Controller
<?php
namespace App\Http\Controllers;
use MarceloCardozo\Antibot\Models\Acesso;
use MarceloCardozo\Antibot\Models\Navegacao;
class AntibotController extends Controller
{
// Listar todos os acessos com paginação
public function index()
{
$acessos = Acesso::latest()->paginate(20);
return view('antibot.index', compact('acessos'));
}
// Detalhes de um acesso específico + suas navegações
public function show(Acesso $acesso)
{
$acesso->load('navegacoes');
return view('antibot.show', compact('acesso'));
}
// Listar apenas bloqueados
public function bloqueados()
{
$bloqueados = Acesso::where('bloqueado', 'true')
->latest()
->paginate(20);
return view('antibot.bloqueados', compact('bloqueados'));
}
// Estatísticas
public function stats()
{
$total = Acesso::count();
$bloqueados = Acesso::where('bloqueado', 'true')->count();
$aprovados = Acesso::where('bloqueado', 'false')->count();
$proxies = Acesso::where('proxy', 'yes')->count();
$vpns = Acesso::where('vpn', 'yes')->count();
$bots = Acesso::where('bot', 'yes')->count();
$hoje = Acesso::whereDate('created_at', today())->count();
$bloqueadosHoje = Acesso::where('bloqueado', 'true')
->whereDate('created_at', today())
->count();
return view('antibot.stats', compact(
'total', 'bloqueados', 'aprovados',
'proxies', 'vpns', 'bots',
'hoje', 'bloqueadosHoje'
));
}
}
Consultas úteis
use MarceloCardozo\Antibot\Models\Acesso;
use MarceloCardozo\Antibot\Models\Navegacao;
// Últimos 10 acessos bloqueados
$bloqueados = Acesso::where('bloqueado', 'true')
->latest()
->take(10)
->get();
// Acessos de um IP específico
$acessos = Acesso::where('ip', '177.0.0.1')->get();
// Verificar se um IP está bloqueado
$estaBloqueado = Acesso::where('ip', '177.0.0.1')
->latest()
->first()
?->isBloqueado(); // true ou false
// Navegação de um acesso (páginas visitadas)
$acesso = Acesso::find(1);
$paginas = $acesso->navegacoes;
// Acessos por país
$porPais = Acesso::select('isocode')
->selectRaw('count(*) as total')
->groupBy('isocode')
->orderByDesc('total')
->get();
// Acessos com proxy ou VPN
$suspeitos = Acesso::where('proxy', 'yes')
->orWhere('vpn', 'yes')
->latest()
->get();
// Total de bloqueados hoje
$total = Acesso::where('bloqueado', 'true')
->whereDate('created_at', today())
->count();
// Acessos de um navegador específico
$chrome = Acesso::where('client_name', 'Chrome')->get();
// Acessos mobile bloqueados
$mobileBloqueados = Acesso::where('device_type', 'smartphone')
->where('bloqueado', 'true')
->get();
Relacionamentos
// Acesso -> Navegacoes (HasMany)
$acesso = Acesso::find(1);
$navegacoes = $acesso->navegacoes; // Collection de Navegacao
// Navegacao -> Acesso (BelongsTo)
$navegacao = Navegacao::find(1);
$acesso = $navegacao->acesso; // Model Acesso
// Eager loading
$acessos = Acesso::with('navegacoes')->latest()->get();
Regras de detecção
O sistema usa 40+ regras com pontuação individual. Quando a soma dos pontos atinge o score_minimo (padrão: 50), o visitante é bloqueado.
Automação direta (80 pts cada)
| Regra | Detecta |
|---|---|
webdriver |
navigator.webdriver === true |
chromedriver |
Variáveis $cdc_ injetadas pelo ChromeDriver |
selenium |
Propriedades globais do Selenium |
puppeteer |
__puppeteer_evaluation_script__ |
playwright |
__playwright, __pw_manual |
cypress |
window.Cypress, window.cy |
phantomjs |
callPhantom, _phantom |
nightmare |
__nightmare |
webdriverio |
wdio, __wdio |
testcafe |
%testCafeDriverInstance% |
browserless |
__browserless, __chrome_aws_lambda |
stack_automacao |
Stack trace com nomes de ferramentas |
Fingerprint e ambiente
| Regra | Pts | Detecta |
|---|---|---|
ua_suspeito |
60 | Keywords de bot no User-Agent |
webdriver_patched |
60 | Undetected-chromedriver (getter customizado) |
chrome_falso |
30 | UA diz Chrome mas window.chrome ausente |
webgl_software |
30 | Renderer software (SwiftShader, Mesa) |
tela_zero |
30 | Dimensões da tela = 0 |
canvas_vazio |
25 | Canvas renderiza imagem vazia |
mobile_sem_touch |
25 | UA mobile sem suporte touch |
sem_color_depth |
20 | Color depth inválido |
platform_mismatch |
20 | UA e navigator.platform divergem |
perm_inconsistente |
20 | Permissões inconsistentes |
audio_vazio |
20 | Audio fingerprint vazio |
Para a lista completa, consulte config/antibot.php.
Desativar uma regra
// config/antibot.php
'regras' => [
'historico_curto' => ['ativo' => false, 'pts' => 5], // desativada
'webdriver' => ['ativo' => true, 'pts' => 80], // ativa
],
Templates de loading
Dois templates disponíveis, configuráveis em config/antibot.php:
spinner— Spinner minimalista (padrão)cloudflare— Visual estilo Cloudflare security check
'tela_carregamento' => 'cloudflare',
Para personalizar, publique as views e edite em resources/views/vendor/antibot/templates/.
Rotas registradas
| Método | URI | Descrição |
|---|---|---|
| GET | /antibot/config |
Configuração pública (JSON) |
| GET | /antibot/api/proxycheck |
Detecção de proxy/VPN |
| GET | /antibot/api/device-detector |
Detecção de dispositivo |
| POST | /antibot/api/salvar |
Salvar registro de acesso |
| GET | /antibot/api/sessao |
Verificar status do visitante |
| POST | /antibot/api/navegacao |
Registrar navegação |
| GET | /antibot/templates/{template} |
Templates (404, spinner, cloudflare) |
O prefixo /antibot é configurável via config('antibot.route_prefix').
Licença
MIT