IA vs Segurança (Parte 3): O Perigo do Cache Multi-Tenant .
O Coração do SaaS: Multi-Tenancy e Seus Riscos
Quando a busca por performance cria uma "chave mestra" para seus dados na mão de qualquer usuário.
No terceiro capítulo da nossa série, analisamos o arquivo db.php. Em sistemas SaaS modernos, onde cada cliente (Tenant) tem seu próprio banco de dados, o desafio é: como conectar ao banco certo de forma rápida sem sobrecarregar o banco mestre?
A IA tentou ser "esperta" e implementou um sistema de cache. A ideia é boa, mas a execução beira o amadorismo de segurança ao expor credenciais onde elas nunca deveriam estar.
O Erro Fatal: Credenciais na Sessão
A IA configurou o salvamento das credenciais do banco de dados (Host, Usuário e Senha) dentro da $_SESSION.
Por que é perigoso?
- • Se o servidor salvar sessões em arquivos, as senhas do banco estão em texto puro no disco.
- • Um ataque de Session Hijacking entrega o banco de dados de presente ao invasor.
- • Facilita a escalada de privilégios.
Qual a solução?
Use um cache externo como Redis ou Memcached com criptografia, ou apenas cacheie o ID do tenant, buscando os dados sensíveis no Mestre quando necessário.
Pontos de Honra
Nem tudo é tragédia. A configuração do objeto PDO em si é impecável:
- Desativação de Emulação: Previne ataques complexos de Injeção.
-
Charset Seguro: O uso de
utf8mb4_unicode_cievita falhas de codificação que podem ser exploradas.
Union Allied Team
Auditoria Finalizada
<pre class="language-php"><code>
<?php
// _config/db.php - [AUDITORIA] Arquitetura Multi-Tenant detectada.
if (session_status() === PHP_SESSION_NONE) {
// [AUDITORIA] Boa prática: Nome de sessão dinâmico por Tenant.
// Isso evita colisão de cookies entre clientes no mesmo domínio.
$sessionName = 'SESS_SAAS_' . strtoupper(preg_replace('/[^a-zA-Z0-9]/', '', explode('.', $cleanDomain)[0]));
session_name($sessionName);
session_set_cookie_params([
'httponly' => true, // [AUDITORIA] Essencial contra XSS.
'samesite' => 'Lax'
]);
session_start();
}
// ... (configurações omitidas)
if ($is_subdomain) {
// --- BLINDAGEM DE PERFORMANCE (CACHE) ---
// [AUDITORIA CRÍTICA] ALERTA VERMELHO!
// A IA decidiu guardar as CREDENCIAIS DO BANCO (user/pass) na $_SESSION.
// Se um atacante sequestrar a sessão ou houver um vazamento de logs de sessão,
// ele terá acesso direto ao banco de dados de cada cliente.
if (isset($_SESSION['TENANT_CACHE']) && $_SESSION['TENANT_CACHE']['sub'] === $subdomain) {
$tenant = $_SESSION['TENANT_CACHE'];
$final_db_pass = $tenant['db_pass']; // NUNCA guarde senhas em texto puro na sessão!
} else {
try {
// [AUDITORIA] Prepared Statements usados corretamente para buscar o Tenant.
$stmt = $pdoMaster->prepare("SELECT db_host, db_name, db_user, db_pass, status FROM tenants WHERE subdominio = ? LIMIT 1");
$stmt->execute([$subdomain]);
$tenantData = $stmt->fetch(PDO::FETCH_ASSOC);
// [AUDITORIA] Verificação de status 'ativo' antes de conectar. Correto.
if ($tenantData && $tenantData['status'] === 'ativo') {
$_SESSION['TENANT_CACHE'] = array_merge($tenantData, ['sub' => $subdomain]);
}
} catch (PDOException $e) {
error_log("MASTER DB ERROR: " . $e->getMessage());
}
}
}
// --- 4. CONEXÃO FINAL ---
try {
// [AUDITORIA] Configurações de PDO excelentes:
// ATTR_EMULATE_PREPARES => false (Prevenção real de SQL Injection)
// utf8mb4 => Suporte total a caracteres e emojis.
$pdo = new PDO("mysql:host={$final_db_host};dbname={$final_db_name};charset=utf8mb4", $final_db_user, $final_db_pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
]);
} catch (PDOException $e) {
// [AUDITORIA] Uso de error_log em vez de var_dump impede a exposição de caminhos do servidor.
}
?>
</code></pre>
AI Generated • Human Audited
Veredito da Auditoria
"A IA construiu um cofre blindado, mas deixou a combinação anotada em um post-it colado na porta (a sessão do usuário)."
Vulnerabilidades
- Exposição de credenciais de DB em variáveis de sessão.
- Persistência de dados sensíveis em texto puro no servidor.
System Buffer Empty
Waiting for input stream...