2

Ótimo texto. Concordo que "atualizei o PHP, então estou seguro" é a ilusão mais perigosa. Pra quem ficou com a pergunta "ok, mas o que eu faço na prática num servidor PHP comum?", deixo algumas camadas baratas que já reduzem bastante o estrago depois que o RCE acontece:

  1. Usuário dedicado e sem privilégio por aplicação. Cada pool do PHP-FPM rodando com um usuário próprio, que não é dono dos arquivos do código. Se o atacante executa código, ele não consegue reescrever a aplicação nem mexer nas outras.
  2. Diretório de upload sem execução. Upload em pasta separada, com o servidor web configurado pra nunca interpretar PHP ali. Isso mata a clássica "subi um .php disfarçado de imagem".
  3. Bloquear saída de rede do servidor web. Quase todo exploit precisa falar com fora (reverse shell, baixar payload, exfiltrar dados). Uma regra de firewall de saída permitindo só o que a aplicação realmente usa (banco, API X, SMTP) quebra muita cadeia de ataque, e é a camada que eu menos vejo em projeto pequeno.
  4. Banco com usuário de menor privilégio. A aplicação não precisa de DROP, GRANT nem acesso a outros schemas. Se a credencial vazar, o estrago fica limitado.
  5. disable_functions e open_basedir. Não são fronteira de segurança (a própria documentação do PHP avisa que open_basedir não deve ser tratado assim, e já existiram bypasses), mas aumentam o custo de um exploit genérico. Encare como lombada, não como muro.
  6. Detecção. Um processo php-fpm criando sh/bash é praticamente sempre sinal de comprometimento. Uma regra de auditoria (auditd ou similar) alertando isso te avisa em minutos, não em meses.

Nenhuma dessas impede o RCE, e esse é justamente o ponto do seu texto: elas assumem que ele vai acontecer e limitam o que o atacante consegue fazer depois.

Carregando publicação patrocinada...
1

Esse comentário parece gerado por IA, mas de qualquer forma são dicas de hardening válidas.

Mas para deixar claro: só hardening não é defesa em profundidade por si só. Fazer hardening é bom, mas isso não é criar uma camada extra de defesa.

Pense em defesa em profundidade como criar várias barreiras de defesa ao invés de confiar na superfície de ataque como sua única barreira de defesa. Comparando:

Sem defesa em profundidade: superfície de ataque -> acesso a sistema.

Com defesa em profundidade: superfície de ataque -> barreira 2 -> barreira 3 -> acesso ao sistema.

Vamos pegar como exemplo a "tua" (ou da IA) dica um: "Usuário dedicado e sem privilégio por aplicação".

Ela é boa e faz sentido. Mas isso não é defesa em profundidade, mesmo que superficialmente possa parecer. Mesmo com limitações de acesso do usuário, ainda assim o atacante já obteve acesso ao sistema. Ele já pode persistir acesso, ele já tem acesso a tua rede interna, já pode tentar explorar uma vulnerabilidade no kernel etc.

Digamos que teu servidor tá hospedado na Google Cloud. Mesmo com um usuário com acesso limitado, o atacante já tem acesso a API de metadados da Google Cloud e, por consequência, já pode obter uma credencial de acesso ao painel da Google Cloud. Com isso ele poderia, potencialmente, obter acesso root não só à esse servidor, mas também todos os outros servidores do projeto. Também obteria acesso à banco de dados e por aí vai.

Para eu realmente considerar como uma "camada de proteção", eu diria que tem que haver isolamento de rede, processos e sistema de arquivos; melhor ainda se houver isolamento de kernel (com MicroVM, por exemplo).

É por isso que mencionei sandboxing e containerezição como exemplos, dá para fazer esse isolamento com os dois. Com SELinux/AppArmor também dá para hardenizar o processo, limitando os acessos dele.

Mas repito: são dicas de hardening válidas. Só queria esclarecer que existe diferença entre hardening e defesa em profundidade.

1

Justo, a distinção faz sentido: o que listei limita o estrago dentro da mesma máquina, mas não cria uma barreira separada.

O exemplo da API de metadados é ótimo, porque é justamente o caminho que um usuário sem privilégio ainda tem. Pra quem está em GCP ou AWS, duas coisas baratas nesse ponto: bloquear 169.254.169.254 no firewall de saída só para o usuário do PHP-FPM (com iptables dá pra filtrar por --uid-owner) e deixar a service account da VM com o mínimo de escopos possível. Na AWS, exigir IMDSv2 também ajuda contra SSRF simples.

Mas concordo: isolamento de verdade é container ou MicroVM com rede e sistema de arquivos separados.