Em resumo: O PHP acede a bases de dados através de duas interfaces muito difundidas: o PDO (PHP Data Objects) trabalha com muitos sistemas de bases de dados através de uma interface uniforme, o mysqli está talhado para MySQL e MariaDB. Com ambos, os prepared statements, isto é, consultas com marcadores de posição, protegem de forma fiável contra SQL injection. Desde o PHP 8.0 (PDO) e 8.1 (mysqli), ambos comunicam por defeito os erros como exceção.
Como o PHP fala com bases de dados
Quase todas as aplicações PHP guardam os seus conteúdos numa base de dados: páginas, encomendas, contas de utilizador, definições. Para isso, o script PHP estabelece uma ligação, envia uma consulta na linguagem SQL e processa o resultado. As credenciais de acesso costumam estar num ficheiro de configuração no mesmo servidor.
Para a gerência isto é importante, porque a base de dados contém em regra os dados mais valiosos da empresa. Quem consegue executar código no servidor web consegue, em regra, também ler o ficheiro de configuração e aceder à base de dados com essas credenciais.
PDO e mysqli em comparação
| Característica | PDO | mysqli |
|---|---|---|
| Bases de dados | muitas, através de controladores como PDO_MYSQL, PDO_PGSQL, PDO_SQLITE, PDO_SQLSRV, PDO_OCI |
MySQL e MariaDB |
| Marcadores de posição | nomeados (:name) e posicionais (?) |
posicionais (?) |
| Estilo | orientado a objetos | orientado a objetos e procedimental |
| Erros como exceção | padrão desde o PHP 8.0.0 | padrão desde o PHP 8.1.0 |
| Forma abreviada para consultas com parâmetros | prepare() e execute() com array |
execute_query() desde o PHP 8.2.0 |
Na prática, decide muitas vezes o framework ou o CMS. Muitos trazem uma camada de base de dados própria que utiliza internamente PDO ou mysqli. Quem assume uma aplicação pode, com uma pesquisa simples no código-fonte, apurar que interface é usada e onde nascem as consultas:
# Onde a aplicação estabelece ligações, onde nascem as consultas?
grep -rn --include='*.php' -E 'new PDO|new mysqli|mysqli_connect|->query\(' /var/www/app/src | head -50
Os resultados com ->query( não são automaticamente inseguros. Mas merecem uma segunda olhadela, porque aí os valores podem ir parar diretamente ao texto SQL, em vez de serem passados por marcadores de posição. Precisamente os projetos mais antigos misturam ambos os estilos, e a verificação compensa antes de qualquer atualização maior.
Prepared statements: o padrão mais importante
Num prepared statement, a consulta é enviada à base de dados com marcadores de posição e os valores são passados em separado. Segundo o manual do PHP, isto protege contra SQL injection, se todas as entradas do utilizador passarem por marcadores de posição.
<?php
declare(strict_types=1);
$pdo = new PDO(
'mysql:host=localhost;dbname=shop;charset=utf8mb4',
$dbUser,
$dbPass,
[PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
);
$stmt = $pdo->prepare('SELECT id, name FROM kunden WHERE plz = :plz AND aktiv = :aktiv');
$stmt->execute(['plz' => $plz, 'aktiv' => 1]);
foreach ($stmt as $zeile) {
echo htmlspecialchars($zeile['name'], ENT_QUOTES, 'UTF-8'), "\n";
}
O mesmo com o mysqli na forma abreviada que existe desde o PHP 8.2:
<?php
$db = new mysqli('localhost', $dbUser, $dbPass, 'shop');
$ergebnis = $db->execute_query('SELECT id, name FROM kunden WHERE plz = ?', [$plz]);
foreach ($ergebnis as $zeile) {
echo htmlspecialchars($zeile['name'], ENT_QUOTES, 'UTF-8'), "\n";
}
Os programadores devem conhecer dois limites. Os marcadores de posição não podem estar entre aspas; nas pesquisas com LIKE, os carateres curinga % pertencem ao valor passado. E os marcadores de posição substituem apenas valores, não nomes de tabelas ou colunas. Como resolver estes casos especiais em segurança mostra-o o guia Prevenir SQL injection em PHP.
Tratamento de erros: exceções em vez de erros silenciosos
Antes, ambas as interfaces trabalhavam por defeito de modo silencioso: uma consulta falhada devolvia false e quem não o verificasse continuava a trabalhar com dados vazios. Isso mudou:
- PDO: Desde o PHP 8.0.0,
PDO::ERRMODE_EXCEPTIONé o padrão. Os erros lançam umaPDOException. - mysqli: Desde o PHP 8.1.0, o relato de erros está definido como
MYSQLI_REPORT_ERROReMYSQLI_REPORT_STRICT. Os erros lançam umamysqli_sql_exception; antes, o relato estava desativado.
Para a segurança, isto significa: as exceções têm de ser capturadas e registadas, mas nunca podem ir parar ao navegador com texto SQL, nomes de tabelas ou credenciais de acesso. Como separar isto de forma limpa descreve-o a página Erros e exceções em PHP.
Atribuir corretamente credenciais e permissões
A base de dados só está tão protegida quanto as suas credenciais de acesso. Estas regras revelaram-se eficazes:
- Utilizador próprio por aplicação: Nunca usar o administrador da base de dados para o site.
- Apenas as permissões necessárias: Um site precisa em regra de ler, escrever, alterar e apagar na sua própria base de dados, mas não de permissões sobre outras bases de dados ou funções do servidor.
- Sem ligação remota sem motivo: A base de dados só deve ser acessível a partir do servidor web, não a partir da internet.
- Credenciais fora da raiz web: Os ficheiros de configuração e
.envnão podem ser acessíveis por URL. Mais sobre isto no artigo Proteger credenciais e ficheiros .env. - Ligação cifrada com servidores separados: Se a base de dados estiver noutra máquina, a ligação deve ser cifrada, para que credenciais e conteúdos não circulem em texto claro pela rede.
- Acessos próprios para manutenção: Cópias de segurança, análises e administradores recebem utilizadores próprios. Assim, é possível seguir no log quem fez o quê e bloquear um acesso sem perturbar o site.
Bases de dados, PHP2Shell e o caso de emergência
O PHP2Shell não afeta a base de dados em si, mas o interpretador PHP: o CVE-2026-109618 e o CVE-2026-109619 permitem a execução de código malicioso sem autenticação. Se isso resultar, o código corre com as permissões do processo PHP e pode, em regra, ler o ficheiro de configuração com as credenciais da base de dados. Os prepared statements não ajudam contra isto, porque o ataque nem sequer passa por uma consulta SQL.
É obrigatória, por isso, a atualização para PHP 8.5.12, 8.4.27, 8.3.36 ou 8.2.35 e o reinício de todos os processos PHP. Se houver indícios de um ataque bem-sucedido, fazem parte do processo novas credenciais de acesso à base de dados, uma verificação da base de dados quanto a conteúdos injetados e uma avaliação da obrigação de notificação nos termos do RGPD. Aqui faz sentido uma resposta a incidentes estruturada.
Altere as palavras-passe da base de dados só depois de limpar o servidor. Enquanto houver código malicioso a correr, ele pode voltar a ler também as novas credenciais.
Como ajuda concretamente a PHP SOS?
- Verificamos como a sua aplicação acede à base de dados e se utilizadores, permissões e acesso de rede correspondem ao mínimo necessário.
- Controlamos se os ficheiros de configuração e de credenciais estão fora da raiz web e não são acessíveis publicamente.
- Depois de um incidente, renovamos as credenciais pela ordem correta e verificamos a base de dados quanto a código injetado.
- Documentamos os resultados de forma que encarregados de proteção de dados e seguradoras os possam acompanhar.
Perguntas frequentes
Devo usar PDO ou mysqli?
Para novos projetos, o PDO é em regra a escolha mais flexível, porque trabalha com muitas bases de dados através da mesma interface. O mysqli faz sentido se utilizar exclusivamente MySQL ou MariaDB e precisar das suas funções especiais. Ambos são seguros se os prepared statements forem usados de forma consequente.
O PDO lança exceções automaticamente em caso de erro?
Sim, desde o PHP 8.0.0 o PDO::ERRMODE_EXCEPTION é o padrão. Antes era o modo silencioso, em que os erros só podiam ser consultados através de errorCode() e errorInfo(). As aplicações mais antigas ainda definem muitas vezes o modo expressamente.
Os marcadores de posição também podem substituir nomes de tabelas ou colunas?
Não. Os marcadores de posição representam apenas valores. Os nomes de tabelas e colunas, por exemplo para uma ordenação, têm de ser verificados contra uma lista fixa de nomes permitidos antes de serem inseridos na consulta.
Depois do PHP2Shell tenho de mudar a palavra-passe da base de dados?
A atualização por si só não o exige. Mas se houver indícios de que o servidor esteve comprometido, todas as credenciais de acesso em ficheiros de configuração consideram-se conhecidas. Nesse caso, deve renová-las depois da limpeza.