UnionAllied Social Branding
Voltar ao Terminal

IA vs Segurança (Parte 2): A Ilusão da Segurança Visual .

Code Audit 27 Fev, 2026
Cover

A Ilusão da Segurança Visual

Quando o Backend esconde segredos obscuros sob uma interface perfeitamente polida.

Se tem uma coisa que as Inteligências Artificiais aprenderam a fazer muito bem, é impressionar os olhos. O arquivo login.php que estamos auditando hoje é um excelente exemplo disso. Ao abrirmos a página, somos recebidos por uma interface construída com Tailwind CSS, animações fluidas, efeitos glassmorphism e cores dinâmicas. Pareceria impecável... se não fôssemos auditar o motor debaixo do capô.

Em nossa segunda análise da série IA vs Segurança, vamos dissecar um script que mistura acertos brilhantes com falhas arquitetônicas e um "pecado capital" na verificação de senhas.

Os Acertos: A IA Fez a Lição de Casa

O código gerado implementa conceitos avançados que muitos desenvolvedores esquecem:

Anti-Brute Force Bloqueia o IP após 5 tentativas falhas em 15 min via tabela login_attempts.
Session Fixation Regenera a sessão com session_regenerate_id() logo após o login.
Gestão de Sessão Limpa o hash da senha da memória (unset) antes de salvar os dados.
Uso de PDO Consultas blindadas com Prepared Statements contra SQL Injection.

O Perigo Mora no "Fallback"

Apesar dos acertos, a IA cometeu um erro clássico. O código tenta usar password_verify(). Porém, se isso falhar, ele faz um fallback e aceita validar a senha em MD5/SHA256 ou até mesmo em texto puro.

Geralmente, a IA gera esse tipo de código para lidar com "sistemas legados". Mas em um ambiente de produção moderno, isso é uma porta escancarada.

Atenção extra: A arquitetura Spaghetti dificulta a manutenção, e a captura de IP via $_SERVER['REMOTE_ADDR'] é ineficaz se a aplicação estiver atrás de um Cloudflare ou Proxy.

Na próxima semana...

Vamos mergulhar no coração dessa aplicação analisando o arquivo db.php. A IA configurou o PDO corretamente? Fique ligado!

Union Allied Team

Union Allied Team

Auditoria Finalizada

login.php
Read Only
<?php
// _auth/login.php 
ob_start(); // [AUDITORIA] Ótima prática. Previne o erro "Headers already sent" ao manipular redirecionamentos.

require_once __DIR__ . '/../_config/db.php';    
require_once __DIR__ . '/../_config/auth.php';

// Carrega configurações do banco pois a sessão ainda não existe
$sysConfig = [];
try {
    $stmtConfig = $pdo->query("SELECT * FROM configuracoes WHERE id = 1 LIMIT 1");
    $dbConfig = $stmtConfig->fetch(PDO::FETCH_ASSOC);
    if ($dbConfig) {
        $sysConfig = $dbConfig;
    }
} catch (Exception $e) {
    // [AUDITORIA ALERTA] Bloco catch vazio (Silent Failure). Se o banco cair, a UI carrega "quebrada" ou com defaults, mas o erro é mascarado.
}

// Redireciona se já logado
if (isset($_SESSION['user']['id'])) {
    header("Location: " . BASE_URL . "/_pages/dashboard.php");
    exit;
}

// Configurações Visuais & Mapeamento (omitidas para brevidade no comentário)
$nomeClinica = !empty($sysConfig['nome_clinica']) ? $sysConfig['nome_clinica'] : 'Sistema Médico';
$logoDb = $sysConfig['logo_path'] ?? '';
$logoPath = !empty($logoDb) ? '../' . $logoDb : '';

$corTema  = !empty($sysConfig['cor_tema']) ? $sysConfig['cor_tema'] : '#4f46e5'; 
$corSide1 = !empty($sysConfig['sidebar_cor_1']) ? $sysConfig['sidebar_cor_1'] : '#312e81';
$corSide2 = !empty($sysConfig['sidebar_cor_2']) ? $sysConfig['sidebar_cor_2'] : '#4338ca';

$error = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // [AUDITORIA] Excelente. Sanitiza o e-mail logo na entrada.
    $email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
    $senha = $_POST['senha'] ?? '';
    
    // [AUDITORIA ALERTA] 'REMOTE_ADDR' pega o IP local se estiver atrás de um Load Balancer/Cloudflare. Requer tratamento com X-Forwarded-For.
    $ip    = $_SERVER['REMOTE_ADDR'];

    try {
        // 1. Anti-Brute Force
        // [AUDITORIA] Excelente lógica de bloqueio baseada no tempo e IP.
        $stmtCheck = $pdo->prepare("SELECT COUNT(*) FROM login_attempts WHERE ip_address = ? AND attempt_time > (NOW() - INTERVAL 15 MINUTE)");
        $stmtCheck->execute([$ip]);
        
        if ($stmtCheck->fetchColumn() >= 5) {
            $error = 'Muitas tentativas. Aguarde 15 minutos.';
        } elseif (empty($email) || empty($senha)) {
            $error = 'Preencha todos os campos.';
        } else {
            // 2. Busca Usuário
            $stmt = $pdo->prepare("SELECT * FROM users WHERE email = ? LIMIT 1");
            $stmt->execute([$email]);
            $user = $stmt->fetch();

            // 3. Verifica Senha
            $senhaCorreta = false;
            if ($user) {
                $stored = $user['password'];
                // [AUDITORIA CRÍTICA] Aqui está o problema! A IA criou um "fallback". 
                // Se não for bcrypt ($2y$), ela tenta SHA256 e, pior, texto puro ($senha === $stored). 
                // Isso nunca deve existir em produção moderna.
                if (strpos($stored, '$2y$') === 0 || strpos($stored, '$2b$') === 0) {
                    if (password_verify($senha, $stored)) $senhaCorreta = true;
                } elseif (hash('sha256', $senha) === $stored) {
                    $senhaCorreta = true; 
                } elseif ($senha === $stored) {
                    $senhaCorreta = true; 
                }
            }

            if ($user && $senhaCorreta) {
                // --- LOGIN BEM SUCEDIDO ---
                if ((int)$user['is_active'] === 0) {
                    $error = 'Conta desativada.';
                } else {
                    // Limpa brute force
                    $pdo->prepare("DELETE FROM login_attempts WHERE ip_address = ?")->execute([$ip]);

                    // Prepara Sessão
                    unset($user['password']); // [AUDITORIA] Ótimo! Nunca trafegar o hash na $_SESSION.
                    $_SESSION['user'] = $user;
                    
                    // --- Geração de Token ---
                    session_regenerate_id(true); // [AUDITORIA] Impede ataque de Session Fixation. Brilhante.
                    $newSessionId = session_id(); 

                    // Grava o token no banco
                    $pdo->prepare("UPDATE users SET session_token = ? WHERE id = ?")->execute([$newSessionId, $user['id']]);
                    
                    // Logs e Limpeza
                    $pdo->prepare("UPDATE login_logs SET status = 'offline', logout_at = NOW() WHERE user_id = ? AND status IN ('active','online')")->execute([$user['id']]);
                    
                    $stmtLog = $pdo->prepare("INSERT INTO login_logs (user_id, ip_address, user_agent, status) VALUES (?, ?, ?, 'active')");
                    $stmtLog->execute([$user['id'], $ip, $_SERVER['HTTP_USER_AGENT']]);
                    $_SESSION['login_log_id'] = $pdo->lastInsertId();

                    // --- TRATAMENTO DE REDIRECIONAMENTO ---
                    $redirectUrl = BASE_URL . '/_pages/dashboard.php';

                    // Roles Específicas
                    if (($user['role'] ?? '') === 'medico') {
                         try {
                            $stmtMed = $pdo->prepare("SELECT id, especialidade FROM medicos WHERE email = ?");
                            $stmtMed->execute([$user['email']]);
                            $medData = $stmtMed->fetch();
                            if($medData) $_SESSION['medico_id'] = $medData['id'];
                        } catch(Exception $e){} // [AUDITORIA ALERTA] Mais um catch vazio.
                        $redirectUrl = BASE_URL . '/_pages/_medico/dashboard.php';
                    }

                    // Forçar troca de senha (prioridade sobre dashboard)
                    if ((int)($user['force_change_password'] ?? 0) === 1) {
                        $_SESSION['force_change'] = true;
                        $redirectUrl = BASE_URL . "/_pages/perfil.php?msg=troca_obrigatoria";
                    }

                    session_write_close(); // [AUDITORIA] Destrava a sessão para requisições paralelas. Excelente em sistemas pesados.
                    header('Location: ' . $redirectUrl);
                    exit;
                }
            } else {
                $error = 'Credenciais inválidas.';
                $pdo->prepare("INSERT INTO login_attempts (ip_address) VALUES (?)")->execute([$ip]);
            }
        }
    } catch (Exception $e) {
        error_log("Login System Error: " . $e->getMessage()); // [AUDITORIA] Boa prática registrar em log interno em vez de expor ao usuário.
        $error = "Erro interno. Tente novamente.";
    }
}
ob_end_flush();
?>
<!-- O restante do código é HTML/Tailwind e não possui falhas lógicas de segurança. -->

AI Generated • Human Audited

Veredito da Auditoria

Risco Médio

"Uma interface deslumbrante escondendo um pecado capital. A validação de senhas compromete um trabalho que tinha tudo para ser perfeito."

Pontos Fortes

  • Proteção Anti-Brute Force por IP
  • Prevenção robusta de Session Fixation
  • Tratamento visual premium (Tailwind)

Vulnerabilidades

  • Fallback para senhas em texto puro
  • Captura de IP frágil (vulnerável a Proxies)
  • Blocos catch silenciosos mascaram erros
Score de Segurança
6.5 /10
SYSTEM_LOGS / usr_input_stream
Live Feed
user@sys:~$
01 02 03

⚠ WARNING: Inputs are sanitized. No HTML allowed.

Timestamp
Payload / Message
_

System Buffer Empty

Waiting for input stream...

Total Logs: 0 Memory Usage: 0.04MB
Whatsapp
Valorizamos sua privacidade Utilizamos cookies para aprimorar sua experiência de navegação, exibir anúncios personalizados e analisar nosso tráfego. Ao clicar em "Aceitar Todos", você concorda com o uso de cookies. Leia nossa Política de Cookies.