File manager - Edit - /var/www/html/parquetecmar/app/Services/FormSecurityService.php
Back
<?php namespace App\Services; use Illuminate\Http\Request; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; /** * Serviço centralizado de proteções do formulário de contato (Fale Conosco). * Responsável por: honeypot, time trap, user-agent, sanitização anti-HTML e validação reCAPTCHA. * Não altera layout; apenas valida e rejeita submissões suspeitas. */ class FormSecurityService { /** @var array<string> Mensagens genéricas para não dar pistas a bots */ public const MESSAGE_INVALID_SUBMISSION = 'Não foi possível processar sua mensagem. Tente novamente.'; /** * Verifica se o honeypot foi preenchido (campo invisível para humanos). * Se preenchido, indica bot → rejeitar. */ public function checkHoneypot(Request $request): bool { $field = config('form-security.honeypot_field', 'website'); $value = $request->input($field); if ($value === null) { return true; // Campo não enviado: ok (pode ser formulário antigo sem o campo) } $value = is_string($value) ? trim($value) : (string) $value; return $value === ''; } /** * Verifica se o usuário permaneceu na página pelo tempo mínimo (time trap). * start_time deve ser timestamp em ms no carregamento da página; rejeita se < min segundos. */ public function checkTimeTrap(Request $request): bool { $startTime = $request->input('start_time'); if ($startTime === null || $startTime === '') { return false; } $startTime = is_numeric($startTime) ? (float) $startTime : 0; $minSeconds = config('form-security.min_seconds_time_trap', 3); $elapsed = (microtime(true) * 1000 - $startTime) / 1000; return $elapsed >= $minSeconds; } /** * Bloqueia user-agents vazios ou suspeitos (bots/scrapers conhecidos). */ public function checkUserAgent(Request $request): bool { $ua = $request->userAgent(); if ($ua === null || trim($ua) === '') { return false; } $suspicious = config('form-security.suspicious_user_agents', []); $uaLower = mb_strtolower($ua); foreach ($suspicious as $needle) { if (str_contains($uaLower, mb_strtolower($needle))) { return false; } } return true; } /** * Remove HTML/tags e detecta conteúdo malicioso. * Retorna a string sanitizada; se houver tags/código suspeito, retorna null (rejeitar). */ public function sanitizeNoHtml(?string $value): ?string { if ($value === null) { return null; } $stripped = strip_tags($value); $stripped = trim($stripped); // Rejeitar se ainda houver sinais de HTML/script (ex.: <, >, <, javascript:) if (preg_match('/<|>|<|>|javascript\s*:|vbscript\s*:|on\w+\s*=/i', $value)) { return null; } return $stripped; } /** * Valida e sanitiza os campos do formulário (sem HTML/conteúdo malicioso). * Retorna array com nome, email, telefone, cpf_cnpj, area_interesse, sugestoes. * Campos obrigatórios nulos indicam rejeição. */ public function sanitizeContactFields(Request $request): array { $nome = $this->sanitizeNoHtml($request->input('nome')); $email = $this->sanitizeNoHtml($request->input('email')); $telefone = $this->sanitizeNoHtml($request->input('telefone')); $cpfCnpj = $this->sanitizeNoHtml($request->input('cpf_cnpj')); $areaInteresse = $request->input('area_interesse'); $areasValidas = config('form-security.areas_interesse', []); if ($areaInteresse !== null && ! in_array($areaInteresse, $areasValidas, true)) { $areaInteresse = null; } $sugestoes = $this->sanitizeNoHtml($request->input('sugestoes')); return [ 'nome' => $nome, 'email' => $email, 'telefone' => $telefone === '' ? null : $telefone, 'cpf_cnpj' => $cpfCnpj === '' ? null : $cpfCnpj, 'area_interesse' => $areaInteresse, 'sugestoes' => $sugestoes, ]; } /** * Valida o token do Google reCAPTCHA no backend. * Se o reCAPTCHA não estiver configurado (sem secret), retorna true (não bloqueia). */ public function validateRecaptcha(Request $request): bool { $config = config('form-security.recaptcha', []); if (empty($config['enabled']) || empty($config['secret_key'])) { return true; } $token = $request->input('g-recaptcha-response'); if (empty($token)) { return false; } try { $response = Http::asForm() ->post('https://www.google.com/recaptcha/api/siteverify', [ 'secret' => $config['secret_key'], 'response' => $token, 'remoteip' => $request->ip(), ]); $body = $response->json(); if (! is_array($body)) { return false; } return ! empty($body['success']); } catch (\Throwable $e) { Log::warning('FormSecurity: reCAPTCHA verification failed', ['message' => $e->getMessage()]); return false; } } /** * Executa todas as verificações de segurança e retorna true se todas passarem. * Caso contrário, retorna false (controller deve redirecionar com mensagem genérica). */ public function runAllChecks(Request $request, array &$sanitizedFields = []): bool { if (! $this->checkHoneypot($request)) { return false; } if (! $this->checkTimeTrap($request)) { return false; } if (! $this->checkUserAgent($request)) { return false; } if (! $this->validateRecaptcha($request)) { return false; } $sanitizedFields = $this->sanitizeContactFields($request); if ($sanitizedFields['nome'] === null || $sanitizedFields['email'] === null || $sanitizedFields['sugestoes'] === null) { return false; } if ($sanitizedFields['area_interesse'] === null) { return false; } return true; } }
| ver. 1.4 |
Github
|
.
| PHP 8.3.28 | Generation time: 0.02 |
proxy
|
phpinfo
|
Settings