storage 3. Conexão Segura com Banco de Dados

A conexão com o banco de dados é o coração da sua aplicação. É aqui que os dados do usuário, transações e toda a informação crítica do sistema residem. Uma conexão mal configurada pode expor sua aplicação a SQL Injection, vazamento de credenciais e perda total de dados.

O PDO (PHP Data Objects) é a biblioteca padrão moderna do PHP para comunicação com bancos de dados. Diferente das antigas funções mysql_* (descontinuadas), o PDO oferece prepared statements, proteção contra injection e suporte a múltiplos bancos (MySQL, PostgreSQL, SQLite).

Nesta seção, você aprenderá a criar uma conexão robusta, com tratamento de erros adequado, prepared statements para prevenir ataques e boas práticas que são exigidas em ambientes profissionais.

warning
⚠️ NUNCA faça isto: Concatenar variáveis diretamente em queries SQL. Exemplo de código vulnerável: $query = "SELECT * FROM users WHERE id = $id". Isto permite SQL Injection e pode destruir seu banco de dados.

vpn_key Por Que PDO e Prepared Statements?

shield

Proteção SQL Injection

Prepared statements separam a lógica SQL dos dados. O banco nunca interpreta dados do usuário como comandos.

swap_horiz

Portabilidade

Trocar de MySQL para PostgreSQL? Apenas mude o DSN. O código permanece o mesmo.

speed

Performance

O banco compila a query uma vez e reutiliza o plano de execução, tornando queries repetidas mais rápidas.

code Criando a Conexão PDO Segura

📁 config/database.php - Conexão com Tratamento de Erros

 PDO::ERRMODE_EXCEPTION, // Lança exceções em erros
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // Array associativo
    PDO::ATTR_EMULATE_PREPARES => false, // Prepared statements nativos
    PDO::ATTR_PERSISTENT => false, // Não usar conexões persistentes
];

// 4. Tentar conexão com tratamento de erro
try {
    $pdo = new PDO($dsn, DB_USER, DB_PASS, $options);
    
    // Log de sucesso (remover em produção)
    // error_log("✅ Conexão PDO estabelecida com sucesso");
    
} catch (PDOException $e) {
    // NUNCA exibir detalhes do erro para o usuário final!
    error_log("❌ Erro PDO: " . $e->getMessage());
    
    // Retornar erro genérico
    http_response_code(500);
    echo json_encode([
        'success' => false,
        'message' => 'Erro ao conectar com o banco de dados'
    ]);
    exit;
}

// Agora $pdo está disponível em toda a aplicação
?>
lightbulb
💡 Dica Profissional: Em produção, use variáveis de ambiente (.env) para armazenar credenciais. Nunca commite senhas no Git. Use bibliotecas como vlucas/phpdotenv para carregar .env.

security Prepared Statements: A Proteção Definitiva

❌ Código Vulnerável (NUNCA use isto)

// ❌ VULNERÁVEL A SQL INJECTION
$id = $_GET['id']; // Pode ser: "1 OR 1=1"
$query = "SELECT * FROM users WHERE id = $id";
$result = $pdo->query($query);

// Um atacante pode enviar: ?id=1 OR 1=1
// Query executada: SELECT * FROM users WHERE id = 1 OR 1=1
// Resultado: TODOS os usuários vazados!

✅ Código Seguro com Prepared Statements

// ✅ SEGURO - Prepared Statement
$id = $_GET['id'];

// 1. Preparar query com placeholder (?)
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');

// 2. Executar com binding de parâmetros
$stmt->execute([$id]);

// 3. Buscar resultado
$user = $stmt->fetch();

// Agora, mesmo que $id seja "1 OR 1=1", o banco trata como string literal
// Query real: SELECT * FROM users WHERE id = '1 OR 1=1'
// Resultado: Nenhum usuário (id não existe), sistema protegido!
info
⚙️ Como Funciona: O PDO envia a query e os dados em duas etapas separadas. Primeiro, o banco compila a estrutura SQL. Depois, recebe os valores como dados puros, nunca interpretados como comandos.

build Métodos Principais do PDO

arrow_forward prepare() + execute()

// Para queries que precisam de dados externos
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$email]);
$user = $stmt->fetch();

playlist_add query()

// Apenas para queries estáticas (sem dados externos)
$stmt = $pdo->query('SELECT * FROM categories');
$categories = $stmt->fetchAll();

add_circle lastInsertId()

// Pegar ID do último INSERT
$stmt = $pdo->prepare('INSERT INTO users (name) VALUES (?)');
$stmt->execute(['João']);
$newId = $pdo->lastInsertId();

format_list_numbered rowCount()

// Contar linhas afetadas (UPDATE/DELETE)
$stmt = $pdo->prepare('DELETE FROM logs WHERE created_at < ?');
$stmt->execute(['2024-01-01']);
$deleted = $stmt->rowCount();

error_outline Tratamento de Erros Profissional

// api/users/get-user.php
prepare('SELECT id, name, email FROM users WHERE id = ?');
    $stmt->execute([$id]);
    $user = $stmt->fetch();
    
    if (!$user) {
        http_response_code(404);
        echo json_encode(['success' => false, 'message' => 'Usuário não encontrado']);
        exit;
    }
    
    echo json_encode(['success' => true, 'data' => $user]);
    
} catch (PDOException $e) {
    // Log detalhado no servidor
    error_log("Erro PDO: " . $e->getMessage());
    
    // Resposta genérica para o cliente
    http_response_code(500);
    echo json_encode(['success' => false, 'message' => 'Erro ao buscar usuário']);
    
} catch (InvalidArgumentException $e) {
    http_response_code(400);
    echo json_encode(['success' => false, 'message' => $e->getMessage()]);
}
?>
check_circle
✅ Boas Práticas:
  • Log detalhado no servidor (error_log())
  • Mensagem genérica para o usuário
  • HTTP status codes corretos (400, 404, 500)
  • Never leak sensitive information
tips_and_updates
🎯 Próxima Seção: Com a conexão segura estabelecida, vamos implementar Autenticação JWT para proteger suas rotas e gerenciar sessões de usuários sem cookies.