Em resumo: Para verificar a base de dados quanto a código malicioso, pesquise uma exportação ou uma cópia, com consultas de leitura, à procura de scripts introduzidos, endereços alheios e código ofuscado em conteúdos e definições. Verifique também triggers, eventos agendados, procedimentos armazenados e utilizadores da base de dados. Limpe só depois da avaliação e nunca com um localizar e substituir às cegas, porque os dados serializados ficariam danificados.
O ponto cego de muitas limpezas
Depois de uma intrusão, a maioria dos operadores verifica os ficheiros. Substituem o núcleo do CMS, eliminam ficheiros PHP suspeitos e voltam a instalar as extensões. O site parece depois limpo e, uma semana mais tarde, volta a reencaminhar para páginas alheias. A razão está muitas vezes na base de dados, que foi aproveitada sem alterações na limpeza.
Para os atacantes, a base de dados é um esconderijo atraente. Um script num widget é entregue em todas as páginas. Uma definição de endereço alterada desvia todos os visitantes. Uma conta de administrador adicional abre a porta para mais tarde. Nada disto salta à vista numa análise de ficheiros.
Depois do PHP2Shell, as vulnerabilidades CVE-2026-109618 e CVE-2026-109619 com execução de código sem autenticação, a base de dados faz por isso parte de qualquer verificação. Quem consegue executar código no servidor pode, em regra, ler da configuração as credenciais da base de dados e escrever nela o que quiser. Como a aplicação comunica com a base de dados é explicado em PHP e bases de dados: PDO e mysqli.
Onde se esconde o código malicioso na base de dados
| Local | Conteúdo típico | Efeito |
|---|---|---|
| Conteúdos (artigos, páginas, produtos) | blocos <script>, ligações invisíveis, <iframe> |
código malicioso ou spam para os visitantes |
| Definições e opções | endereço do site alterado, scripts alheios em widgets ou opções do tema | redirecionamentos, scripts em todas as páginas |
| Tabelas de utilizadores | administradores adicionais | acesso permanente |
| Triggers | código que altera conteúdos a cada gravação | o código malicioso regressa após a limpeza |
| Eventos agendados | tarefas recorrentes no servidor de base de dados | alterações sem acesso web |
| Utilizadores da base de dados | contas adicionais com direitos alargados | acesso à margem do CMS |
As últimas três linhas são particularmente esquecidas, porque não estão nas tabelas do CMS, mas no próprio servidor de base de dados.
Nas lojas online, um local merece atenção especial: as definições através das quais são incluídos scripts adicionais no checkout e na conclusão da encomenda. Um script alheio nesse ponto pode ler os dados introduzidos pelos clientes antes de seguirem cifrados para o prestador de pagamentos. Por isso, verifique de forma dirigida todos os campos de código de cabeçalho e rodapé, as inclusões de rastreio e as definições de design que atuam nas páginas de checkout.
Pense ainda noutras bases de dados no mesmo servidor. Cópias de teste e de staging, estados antigos de projetos ou bases de dados de outros sites estão muitas vezes ao lado e acessíveis com as mesmas credenciais. O que for encontrado na base de dados em produção deve ser verificado também nelas, antes de uma cópia voltar a servir de base mais tarde.
Verificar a base de dados: conteúdos e definições
Trabalhe numa exportação. É simultaneamente preservação de provas e base de pesquisa. A exportação pode ser pesquisada como ficheiro de texto, sem sobrecarregar a base de dados em funcionamento:
# Criar uma exportação consistente (as credenciais são pedidas)
mysqldump --single-transaction --routines --triggers --events -u BENUTZER -p DATENBANK > export.sql
sha256sum export.sql > export.sql.sha256
# Contar padrões típicos na exportação
grep -oiE '<script[^>]{0,80}' export.sql | sort | uniq -c | sort -rn | head -30
grep -oiE '(eval\(|base64_decode|fromCharCode|document\.write|atob\()' export.sql | sort | uniq -c
grep -oiE 'https?://[a-z0-9.-]+' export.sql | sort | uniq -c | sort -rn | head -50
A última consulta lista, por frequência, todos os domínios que aparecem na base de dados. Os domínios alheios que ninguém da equipa conhece são os candidatos mais importantes. Para consultas dirigidas na própria base de dados, servem comandos SELECT. O exemplo usa tabelas do WordPress; o prefixo e os nomes das tabelas de outros sistemas constam da respetiva documentação:
-- Definições com scripts ou funções suspeitas
SELECT option_name, LENGTH(option_value) AS laenge
FROM wp_options
WHERE option_value LIKE '%<script%'
OR option_value LIKE '%eval(%'
OR option_value LIKE '%base64_decode%'
ORDER BY laenge DESC;
As entradas com conteúdo muito grande merecem atenção especial. O guia Encontrar backdoors no WordPress oferece contexto sobre esconderijos específicos do WordPress.
Triggers, eventos e contas da base de dados
Em muitas aplicações web, os triggers e eventos agendados nem sequer estão previstos. Se existirem, alguém deve saber explicar porquê. Estas consultas apenas leem das tabelas de sistema do MySQL e do MariaDB:
-- Triggers em todas as bases de dados
SELECT TRIGGER_SCHEMA, TRIGGER_NAME, EVENT_OBJECT_TABLE, CREATED
FROM information_schema.TRIGGERS;
-- Eventos agendados
SELECT EVENT_SCHEMA, EVENT_NAME, STATUS, CREATED, LAST_EXECUTED
FROM information_schema.EVENTS;
-- Procedimentos e funções armazenados
SELECT ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_TYPE, CREATED
FROM information_schema.ROUTINES
WHERE ROUTINE_SCHEMA NOT IN ('mysql', 'sys', 'information_schema', 'performance_schema');
-- Utilizadores da base de dados
SELECT user, host FROM mysql.user ORDER BY user;
Se a sua aplicação usar PostgreSQL ou outro sistema de base de dados, as tabelas de sistema e os comandos têm outros nomes; o princípio mantém-se. Para a análise, recomenda-se um utilizador de base de dados próprio, apenas com permissão de leitura. Assim, nem um erro de escrita numa consulta pode alterar nada, e a verificação não deixa vestígios nos dados.
Um trigger que acrescenta código ao gravar um artigo explica por que razão o código malicioso regressa após cada limpeza. Os utilizadores de base de dados desconhecidos fazem parte da verificação de contas tal como as contas de administrador no CMS; veja mais em Encontrar contas de administrador alheias no CMS e no servidor.
Limpar corretamente, sem destruir dados
A limpeza é a parte mais delicada. Esta ordem tem-se revelado eficaz:
- Avaliar os resultados: cada ocorrência é classificada como conhecida, explicável e inofensiva ou alheia. Os códigos de rastreio e de mapas são muitas vezes legítimos.
- Fechar a porta de entrada: levar o PHP para uma versão corrigida (8.5.12, 8.4.27, 8.3.36, 8.2.35 ou backport confirmado), reiniciar todos os workers, atualizar a aplicação.
- Remover de forma dirigida: limpar os conteúdos alheios através da aplicação ou com ferramentas que tratem corretamente os dados serializados. Eliminar triggers, eventos e contas alheios.
- Mudar as credenciais: nova palavra-passe da base de dados, a registar na configuração da aplicação, e limitar os direitos do utilizador da aplicação à sua própria base de dados.
- Voltar a verificar: repetir as consultas de pesquisa passados alguns dias.
Não substitua nada com localizar e substituir na exportação SQL. Os dados PHP serializados guardam o comprimento de cada cadeia de caracteres. Se o comprimento mudar, a definição torna-se ilegível e o site pode deixar de funcionar.
Os redirecionamentos que surjam apesar de a base de dados estar limpa têm a causa noutra camada. O artigo O site reencaminha para páginas alheias ajuda a delimitá-la.
Como ajuda concretamente a PHP SOS?
- Preservamos a base de dados com soma de verificação e pesquisamos na exportação scripts, domínios alheios e código ofuscado.
- Verificamos triggers, eventos, procedimentos e utilizadores da base de dados que estão fora do CMS.
- Limpamos os resultados no âmbito da remoção de malware, sem danificar dados serializados.
- Limitamos os direitos do utilizador da aplicação e repetimos a verificação após a limpeza.
Perguntas frequentes
Pode haver código malicioso na base de dados?
Sim. Os atacantes escrevem JavaScript em artigos, descrições de produtos ou widgets, alteram definições de endereço para redirecionamentos ou criam contas de administrador. No MySQL e no MariaDB, podem ainda alterar dados através de triggers e eventos agendados. Uma análise de ficheiros não encontra nada disto.
Como pesquiso a base de dados sem estragar nada?
Trabalhe numa exportação ou numa cópia e use apenas consultas SELECT. Uma exportação pode também ser pesquisada com grep como ficheiro de texto. As alterações só se fazem depois de avaliado cada resultado e de existir uma cópia de segurança.
Por que não devo limpar os resultados com localizar e substituir na exportação?
Muitos CMS guardam definições como dados PHP serializados, nos quais o comprimento de cada cadeia de caracteres também é guardado. Uma substituição altera o comprimento, mas não a indicação, e a definição torna-se ilegível. Limpe através da aplicação ou com ferramentas que compreendam dados serializados.
Todo o script na base de dados é suspeito?
Não. Códigos de rastreio, mapas, vídeos ou widgets de chat também são guardados como script. O decisivo é saber se alguém da equipa conhece a entrada e o endereço de destino.