O WordPress é um dos sistemas mais populares do mundo para criação de sites e blogs, mas também é alvo constante de ataques. Plugins desatualizados, temas comprometidos e permissões indevidas podem abrir brechas de segurança.
Neste artigo, você vai aprender a:
- Identificar sinais de invasão;
- Remover malwares como backdoors;
- Reinstalar com segurança o núcleo do WordPress;
- Proteger pastas e arquivos sensíveis;
- Automatizar auditorias com um script prático.
E o melhor: sem perder seu conteúdo. Vamos lá?
🚨 Sinais de que seu WordPress foi comprometido
Você deve ficar atento se notar:
- Redirecionamentos estranhos ao acessar seu site;
- Lentidão ou picos de tráfego não usuais;
- Aparecimento de arquivos desconhecidos em
/wp-content/; - Inclusões PHP suspeitas, como
@include("/var/www/html/...").
🔐 Boas práticas de segurança
Antes de automatizarmos tudo, vale relembrar as boas práticas fundamentais:
- Atualize sempre seu WordPress, plugins e temas.
- Nunca use plugins piratas (nulled) — eles são porta de entrada para códigos maliciosos.
- Use senhas fortes e autenticação em duas etapas.
- Aplique permissões corretas: 755 para pastas e 644 para arquivos.
- Proteja arquivos críticos com
chattr +i. - Use um firewall de aplicação (WAF), se possível.
⚙️ Automatizando a verificação e recuperação
Criamos um script completo em bash para Linux, que faz tudo de forma automatizada:
- Destrava o WordPress;
- Remove possíveis backdoors;
- Reinstala os diretórios principais;
- Verifica a integridade com
wp-cli; - Apaga
.htaccesssuspeitos e recria o padrão; - Audita temas com base em listas brancas;
- Protege arquivos e envia um relatório por e-mail.
📦 Pré-requisitos
Antes de executar o script:
- Acesse o servidor via SSH;
- Tenha o
wp-cliinstalado e funcionando com permissões dewww-data; - Certifique-se de ter
mailutilsinstalado para envio de e-mails (sudo apt install mailutils); - Faça backup completo do site (banco e arquivos).
🧪 Como usar o script
- Salve o conteúdo abaixo como
audit-total-v3.sh; - Dê permissão de execução :
chmod +x audit-total-v3.sh
3. Execute com o caminho para o diretório do WordPress:
./audit-total-v3.sh /var/www/html/seusite
🧾 Script completo: audit-total-v3.sh
#!/bin/bash
# audit-total-v3.sh
# Auditoria e reforço de segurança para WordPress
# Autor: Osvaldo Janeri Filho
# ========================== CONFIGURAÇÕES ==========================
EMAIL="[email protected]"
# Verifica argumentos
if [ -z "$1" ]; then
echo "Erro: Caminho do WordPress não informado."
echo "Uso: $0 /caminho/do/wordpress [--run]"
exit 1
fi
WP_PATH="$1"
LOG_FILE="$WP_PATH/auditoria-$(date +%F).log"
SUBJECT="Relatório de Auditoria do WordPress"
# Detecta modo de execução
if [ "$2" == "--run" ]; then
DRY_RUN=false
else
DRY_RUN=true
fi
declare -A WHITELIST
# ========================== CORES E LOGS ==========================
txtInfo="\e[32m"
txtError="\e[31m"
txtWarn="\e[33m"
txtReset="\e[0m"
char_success="✅"
char_fail="❌"
char_alert="⚠️"
char_info="ℹ️"
function IO:success() { echo -e "${txtInfo}${char_success} $*${txtReset}" | tee -a "$LOG_FILE"; }
function IO:error() { echo -e "${txtError}${char_fail} $*${txtReset}" | tee -a "$LOG_FILE"; }
function IO:alert() { echo -e "${txtWarn}${char_alert} $*${txtReset}" | tee -a "$LOG_FILE"; }
function IO:info() { echo -e "${txtInfo}${char_info} $*${txtReset}" | tee -a "$LOG_FILE"; }
# ========================== EXECUTOR CONDICIONAL ==========================
run() {
local cmd="$1"
local desc="$2"
if $DRY_RUN; then
IO:info "[dry-run] Comando: $cmd"
IO:info "[dry-run] $desc"
else
eval "$cmd"
fi
}
# ========================== FUNÇÕES DE SEGURANÇA ==========================
lock_wordpress() {
if [ "$1" == "on" ]; then
IO:info "Aplicando travas ao WordPress..."
run "chown -R root $WP_PATH" "Assume propriedade como root"
run "chmod g-w $WP_PATH" "Remove permissão de escrita para grupo"
run "chmod g+xr $WP_PATH" "Garante leitura/execução do diretório"
run "find $WP_PATH -type d -exec chmod 555 {} \;" "Permissão de leitura/execução para pastas"
run "find $WP_PATH -type f -exec chmod 444 {} \;" "Permissão de somente leitura para arquivos"
run "find $WP_PATH/wp-content/cache -type d -exec chmod 775 {} \; &>/dev/null" "Permissões especiais para cache (pastas)"
run "find $WP_PATH/wp-content/cache -type f -exec chmod 664 {} \; &>/dev/null" "Permissões especiais para cache (arquivos)"
run "chmod -f 664 $WP_PATH/wp-content/wp-cache-config.php" "Permissões corretas para wp-cache-config.php"
run "chattr +i $WP_PATH/wp-content/plugins" "Imutável: plugins"
run "chattr +i $WP_PATH/wp-content/themes" "Imutável: temas"
run "chattr +i $WP_PATH/wp-admin" "Imutável: painel admin"
run "chattr +i $WP_PATH/wp-config.php" "Imutável: wp-config.php"
run "chattr +i $WP_PATH/index.php" "Imutável: index.php"
run "chattr +i $WP_PATH/wp-settings.php" "Imutável: wp-settings.php"
run "chattr -i $WP_PATH/wp-content" "Remove imutabilidade de wp-content"
run "chattr -i $WP_PATH/wp-content/uploads" "Remove imutabilidade de uploads"
run "chmod g+rw $WP_PATH/wp-content/uploads" "Permite escrita em uploads"
run "chmod 755 $WP_PATH/wp-content/uploads -R" "Permissões corretas em uploads"
run "chown www-data:www-data $WP_PATH/wp-content/wmufs-temp" "Permissões corretas no temp"
IO:success "WordPress travado com sucesso."
elif [ "$1" == "off" ]; then
IO:info "Removendo travas do WordPress..."
run "chattr -i $WP_PATH" "Remove imutabilidade do diretório"
run "chattr -i $WP_PATH/*" "Remove imutabilidade de arquivos principais"
run "chown -R www-data $WP_PATH" "Retorna propriedade ao www-data"
run "find $WP_PATH -type d -exec chmod 755 {} \;" "Permissão padrão de pastas"
run "find $WP_PATH -type f -exec chmod 644 {} \;" "Permissão padrão de arquivos"
run "chattr -i $WP_PATH/wp-content/plugins" "Destrava plugins"
run "chattr -i $WP_PATH/wp-content/themes" "Destrava temas"
IO:success "WordPress destravado com sucesso."
fi
}
# ========================== BACKDOOR CHECK ==========================
procura_arq_includes() {
IO:info "Procurando por backdoors..."
search_strings=("@include (\"/var/www/html/")
find $WP_PATH -type f | while read -r file; do
for str in "${search_strings[@]}"; do
if grep -q "$str" "$file"; then
IO:alert "Backdoor encontrado em: $file"
secondary_file=$(grep "@include" "$file" | cut -f 2 -d '"')
if [ -f "$secondary_file" ]; then
run "rm '$secondary_file'" "Remove arquivo malicioso incluído"
fi
if ! $DRY_RUN; then
grep -v "$str" "$file" > temp && mv temp "$file"
else
IO:info "[dry-run] Remover linha com '$str' de $file"
fi
IO:success "Backdoor removido de: $file"
fi
done
done
}
# ========================== TEMA - LISTA BRANCA ==========================
gerar_lista_branca() {
IO:info "Gerando lista branca de arquivos por tema..."
sudo -u www-data wp theme list --field=name --path=$WP_PATH | while read -r theme; do
theme_path=$(sudo -u www-data wp theme path "$theme" --path=$WP_PATH)
if [ -d "$theme_path" ]; then
WHITELIST[$theme]="$(find "$theme_path" -type f -printf "%P\n")"
IO:success "Lista branca para '$theme'."
else
IO:error "Tema '$theme' não encontrado."
fi
done
}
verificar_arquivos_temas() {
IO:info "Verificando arquivos não permitidos nos temas..."
for theme in "$WP_PATH/wp-content/themes/"*/; do
theme_name=$(basename "$theme")
if [[ -n "${WHITELIST[$theme_name]}" ]]; then
IFS=$'\n' read -r -d '' -a allowed_files <<< "${WHITELIST[$theme_name]}"
find "$theme" -type f | while read -r file; do
relative_path="${file#$theme}"
if [[ ! " ${allowed_files[@]} " =~ " $relative_path " ]]; then
IO:alert "Arquivo suspeito em $theme_name: $file"
run "rm '$file'" "Remoção de arquivo fora da lista branca"
fi
done
else
IO:alert "Tema '$theme_name' sem lista branca. Pulando."
fi
done
}
# ========================== .HTACCESS E PHP.INI ==========================
adicionar_htaccess_padrao() {
IO:info "Criando .htaccess padrão..."
if ! $DRY_RUN; then
cat <<EOL > "$WP_PATH/.htaccess"
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
EOL
else
IO:info "[dry-run] Recriar .htaccess padrão"
fi
IO:success ".htaccess preparado."
}
# ========================== RELATÓRIO FINAL ==========================
gerar_relatorio() {
IO:info "Enviando relatório por e-mail..."
sed 's/\x1b\[[0-9;]*m//g' "$LOG_FILE" | mail -a "Content-Type: text/plain; charset=UTF-8" -s "$SUBJECT" "$EMAIL"
IO:success "Relatório enviado para $EMAIL."
}
# ========================== AUDITORIA COMPLETA ==========================
audit_total() {
lock_wordpress off
procura_arq_includes
run "chattr -i $WP_PATH/php.ini" "Destrava php.ini"
run "rm -f $WP_PATH/php.ini" "Remove php.ini para recriar"
run "find $WP_PATH -type f -name '.htaccess' -delete" "Remove .htaccess antigos"
IO:info "Verificando checksums..."
if ! $DRY_RUN; then
sudo -u www-data wp core verify-checksums --path=$WP_PATH >> $LOG_FILE 2>&1
else
IO:info "[dry-run] Verificação de integridade do WordPress"
fi
run "rm -rf $WP_PATH/wp-admin" "Remove wp-admin"
run "rm -rf $WP_PATH/wp-includes" "Remove wp-includes"
run "sudo -u www-data wp core download --force --locale=pt_BR --path=$WP_PATH" "Reinstala o núcleo do WordPress"
IO:info "Criando php.ini seguro..."
if ! $DRY_RUN; then
echo "disable_functions = exec,shell_exec,system,passthru,pcntl_exec,proc_open" > $WP_PATH/php.ini
else
IO:info "[dry-run] Criar php.ini com funções perigosas desabilitadas"
fi
run "chattr +i $WP_PATH/php.ini" "Trava php.ini"
gerar_lista_branca
verificar_arquivos_temas
adicionar_htaccess_padrao
run "chattr +i $WP_PATH/index.php" "Trava index.php"
run "chattr +i $WP_PATH/wp-settings.php" "Trava wp-settings.php"
run "chattr +i $WP_PATH/wp-config.php" "Trava wp-config.php"
run "chattr +i $WP_PATH/wp-content/plugins" "Trava plugins"
run "chattr +i $WP_PATH/wp-content/themes" "Trava temas"
lock_wordpress on
gerar_relatorio
IO:success "Auditoria completa."
}
# ========================== EXECUÇÃO ==========================
audit_total
✉️ O que acontece ao final?
O script:
- Rodando apenas com o diretório nos argumentos, ele irá rodar em modo — dry-run, ou seja, sem modificar nada
- Para rodar e executar as ações, rode como ./script.sh caminho-do-wordpress — run
- Envia um relatório com tudo que foi encontrado e corrigido;
- Reforça a proteção dos arquivos principais;
- Restaura a integridade da instalação;
- E deixa o site rodando com segurança sem comprometer seu conteúdo, banco de dados ou uploads.
✅ Conclusão
Manter seu WordPress seguro não precisa ser um bicho de sete cabeças. Com automação, boas práticas e atenção aos sinais, é possível blindar sua instalação sem perder produtividade.
Salve este script, agende auditorias periódicas e durma mais tranquilo sabendo que seu site está protegido.
Se você gostou deste artigo, deixe um comentário, compartilhe e acompanhe novas dicas sobre segurança da informação!