Skip to main content

5 maneiras de prevenir a injeção de código PHP

Escrito por
Headshot of DeveloperSteve Coochin

DeveloperSteve Coochin

social php security

29 de junho de 2021

0 minutos de leitura

Dando continuidade ao meu post anterior sobre como testar vulnerabilidades de segurança no PHP Composer, achei que este post poderia ajudar a criar aplicativos mais seguros e prevenir a injeção de código PHP. Como desenvolvedores, criamos aplicativos para facilitar a vida dos usuários finais. Sejam eles de entretenimento, trabalho ou redes sociais, o objetivo final é proteger as pessoas para quem desenvolvemos, incorporando segurança ao código. Adotar práticas de programação segura não só ajuda a manter os usuários finais protegidos, como também economiza tempo e custos de desenvolvimento ao evitar retrabalho.

O que é injeção de código?

Injeção de código é um ataque que insere uma carga de código malicioso por meio de um vetor de ataque vulnerável. O objetivo é comprometer a integridade do aplicativo visado. O invasor pode enviar código PHP executável ou JavaScript, que será executado no ambiente de execução do aplicativo ou no navegador do usuário final. Vale lembrar que o PHP não oferece um mecanismo de escape específico para JavaScript, o que também pode causar problemas. A vulnerabilidade é processada no ambiente de execução do PHP, seja no servidor, seja no navegador de um usuário desavisado.

Como prevenir a injeção de código em PHP

A segurança no desenvolvimento começa no código, com a adoção de uma convenção de programação segura. Fique de olho nas entradas expostas ao usuário e em como elas são tratadas. Higienize todos os dados recebidos e saiba como eles são armazenados no aplicativo.

Como regra fundamental, nunca permita a execução dinâmica de código em nenhum aplicativo. Por exemplo, evite, sempre que possível, usar funções nativas do PHP como shell_exec() e exec(), que são executadas no nível do sistema operacional.

É altamente recomendável usar sempre uma ferramenta de análise de segurança de código aberto, como o Snyk Open Source, que analisa seu gerenciador de pacotes para identificar possíveis vulnerabilidades em bibliotecas e dependências. Também tenho consultado diariamente o composer Snyk Vulnerability Database para conhecer as técnicas utilizadas. As páginas detalhadas dos relatórios permitem rastrear os repositórios até o nível do código e entender como as vulnerabilidades foram identificadas.

5 maneiras de prevenir a injeção de código no desenvolvimento de aplicativos PHP

  1. Evite usar exec(), shell_exec(), system() ou passthru()

  2. Evite usar strip_tags() para higienizar dados

  3. Serialização de código

  4. Use um linter de segurança para PHP

  5. Use uma ferramenta SAST para identificar problemas de injeção de código

1. Evite usar exec(), shell_exec(), system() ou passthru()

Como diz o ditado: “aqui há dragões”. Como regra, evite, sempre que possível, usar qualquer recurso que chame diretamente o ambiente operacional pelo PHP. Do ponto de vista dos vetores de ataque, isso abre muitas possibilidades para introduzir problemas de todo tipo na sua infraestrutura.

Tradicionalmente, funções como exec(), shell_exec(), system(), e passthru() eram usadas para tarefas como compactar ou descompactar arquivos, criar tarefas cron e até navegar por arquivos e pastas do sistema operacional. Todas são executadas sem nenhum mecanismo integrado de higienização do código. É aí que começam os problemas, principalmente quando são usadas com entradas diretas do usuário que não foram validadas ou higienizadas.

O PHP oferece alguns operadores funcionais com escape de entrada integrado, como escapeshellcmd() e escapeshellarg(), que aplicam um nível de escape e higienização à entrada durante a chamada da função. Há maneiras mais seguras de obter os mesmos resultados.

A partir do PHP 7.4, o arquivamento pode ser feito pela classe ZipArchive, ativada com a flag --with-zip durante a compilação do PHP. Ainda assim, é preciso ter cuidado, pois isso pode permitir um ataque de travessia de diretório.

Interaja com o sistema de arquivos por meio de código e reduza as formas de acessá-lo diretamente por entradas do usuário. O PHP tem funções de arquivo que podem ser usadas sem chamar diretamente o sistema operacional. Por exemplo, você pode obter uma lista de arquivos em um diretório específico com a função scandir(), que retorna um array com os arquivos disponíveis no diretório, para que sejam abertos ou referenciados no próprio código. Tenha cuidado para não acessar arquivos criados a partir de entradas expostas e também com algumas funções relacionadas às permissões de arquivos, como chmod() e chown(). Além disso, não confie no cabeçalho do conteúdo (que pode ser falsificado) e valide o tipo de arquivo.

Crie tarefas cron dinamicamente usando uma biblioteca do Composer, como cron. Na prática, isso é executado por uma tarefa cron, que então executa suas próprias tarefas como parte da execução geral. Ainda é preciso ter cuidado com o que será executado, mas essa abordagem permite criar processos de forma mais programática, sem expor o acesso ao sistema operacional.

2. Evite usar strip_tags() para higienizar dados

Tenha sempre muito cuidado ao higienizar e tratar entradas do usuário. O objetivo é aceitar entradas válidas e armazená-las e tratá-las corretamente, para que seu aplicativo não fique vulnerável. Lembre-se: as entradas são um vetor de ataque aberto, que pessoas mal-intencionadas podem usar para interagir com seu aplicativo. Portanto, reserve um tempo para sempre tratar os dados de entrada corretamente.

A função strip_tags() foi criada principalmente para remover apenas HTML e PHP das entradas fornecidas. Isso significa que JavaScript, SQL e outras entradas potencialmente maliciosas serão consideradas válidas pela função. htmlentities() é outra opção usada para higienizar entradas e também permite definir conjuntos de caracteres UTF. Lembre-se, porém, de que ela não higieniza completamente os dados.

Também é possível filtrar os dados com uma função de expressão regular, como preg_replace('/[^a-zA-Z0-9]+/', '', $text). Para conjuntos de caracteres UTF-8, ela retorna somente letras e números da entrada. Também é preciso ter cuidado com funções como mb_strtolower() ao higienizar entradas do usuário. Já vimos vulnerabilidades em algumas funções de strings multibyte (mb_), como a CVE-2020-7065, uma vulnerabilidade de gravação fora dos limites causada pela codificação UTF-32LE. Isso pode sobrescrever a pilha, causar uma falha de buffer e permitir a execução de código.

Prefira algo como filter_var(), que valida e higieniza os dados com base nas opções de filtro definidas. Por exemplo, FILTER_SANITIZE_STRING e FILTER_FLAG_STRIP_HIGH, passados para a função filter_var() junto com a entrada do usuário, removem todas as tags HTML e os caracteres com valor ASCII > 127.

Há também algumas bibliotecas do Composer comumente usadas para higienizar entradas. Uma biblioteca como HTML purifier oferece higienização compatível com HTML e uma lista de permissões bem abrangente, que você pode personalizar de acordo com os tipos de dados que seu aplicativo precisa aceitar.

3. Evite unserialize() em PHP

Esta seção poderia facilmente ser um post inteiro e, ao longo dos anos, o tema gerou muito debate, inclusive entre os desenvolvedores que trabalham com PHP. O próprio manual do PHP destaca os perigos do uso da função unserialize(). Em especial, recomenda que ela “não receba entradas de usuários não confiáveis”, pois isso pode fazer com que “o código seja carregado e executado”.

A função Unserialize() serve para converter uma classe em uma string que pode ser armazenada e passada para outras funções ou armazenada em cache para uso posterior. À primeira vista, parece relativamente inofensiva. Mas, quando você entende como o código C subjacente funciona ao armazenar dados de unserialize() na memória, fica mais fácil perceber onde podem surgir muitos dos problemas.

É altamente recomendável usar um formato de dados padrão, como JSON, por meio de json_decode() e json_encode(). Isso pode oferecer um método seguro e higienizado para enviar e receber dados serializados do usuário.

4. Use um linter de segurança para PHP

Contar com as ferramentas certas no fluxo de desenvolvimento ajuda a criar código seguro e funcional desde o início. Linters são ótimos para reduzir erros e possíveis problemas no desenvolvimento de aplicativos PHP, além de diminuir as vulnerabilidades no código-fonte.

Também é altamente recomendável desativar por padrão a exibição de erros nas configurações do PHP.ini. Desabilitar error_reporting = E_ALL, ~E_NOTICE e ~E_WARNING remove mensagens de erro que poderiam ser usadas para identificar informações sobre o ambiente e a configuração do aplicativo.

A própria linguagem PHP inclui um linter que exibe mensagens de erro bem detalhadas durante a validação. Você pode executá-lo pela CLI (ou como parte de frameworks de teste) com o comando PHP -l, seguido do arquivo a ser verificado. A desvantagem é que ele só consegue verificar um arquivo por vez, embora possa ser usado em um loop para percorrer vários arquivos em uma única execução.

O PHPlint é uma opção robusta e conhecida para verificar vários arquivos rapidamente. Ele pode ser usado pela CLI ou como uma biblioteca acionada pelo Composer. Outra opção é executá-lo facilmente em uma imagem Docker. O PHPlint pode verificar código PHP 7 e PHP 8. Embora gere mensagens bastante detalhadas, ele permite identificar problemas com vários processos de lint ao mesmo tempo.

Outro linter popular é o PHP-Parrallel-Lint, compatível com PHP 5.3 a PHP 8.0 e capaz de executar vários processos de lint mais rápido do que você consegue digitar echo "hello world". O PHP-Parrallel-Lint fornece resultados mais detalhados do que as opções mencionadas anteriormente, embora ainda não ofereça uma opção de CLI nem uma solução Docker pronta para uso.

5. Use uma ferramenta de teste estático de segurança de aplicativos para identificar problemas de injeção de código

O PHPStorm é um dos IDEs mais usados no desenvolvimento em PHP e inclui recursos muito úteis de análise estática de código. Além de oferecer os linters que vimos acima, ele se integra ao Xdebug, ótimo para analisar o código, criar perfis de desempenho e, como o nome sugere, depurar o código para reduzir erros. Para fluxos de trabalho de programação segura, o PHPStorm permite aplicar regras específicas de código e padrões PSR, o que é útil, principalmente no desenvolvimento de aplicativos em conformidade com o PCI.

Para melhorar a segurança do código, o Snyk tem um plugin para PHPStorm que ajuda a analisar problemas de qualidade e vulnerabilidades no código e nas bibliotecas usadas. É fácil de instalar e começar a usar, e está disponível na seção de plugins do aplicativo.

Marketplace de plugins do PhpStorm exibindo resultados de busca por Snyk Vulnerability Scanner e a descrição da vulnerabilidade de segurança

Outra ferramenta de desenvolvimento que vem sendo cada vez mais adotada por desenvolvedores, especialmente os que trabalham com PHP, é o VSCode. Assim como o PHPStorm, ele também inclui um linter e permite aplicar os padrões PSR.

Analisar vulnerabilidades no VSCode com o Snyk é simples: basta abrir um novo terminal do VSCode e usar a Snyk CLI para executar o comando snyk test ou usar o snyk monitor para receber alertas contínuos sobre a base de código. Para saber mais e começar a usar a Snyk CLI com PHP, confira meu post anterior sobre como testar vulnerabilidades de segurança no PHP Composer com o Snyk.

Atualização: também lançamos recentemente o suporte a PHP no Snyk Code. Para saber mais e experimentar, confira o post O suporte do Snyk Code à análise de vulnerabilidades em PHP entra em beta.

Resumo

Como desenvolvedores, criamos aplicativos para ajudar os usuários finais no dia a dia. Garantir que eles estejam protegidos não só dá às pessoas a certeza de que seus dados estão seguros, como também permite que confiem em você.

Identificar vulnerabilidades de injeção de código durante o desenvolvimento de aplicativos é simples, desde que você conte com as ferramentas e o fluxo de trabalho certos desde o início.

Comece a jogar Capture the Flag

Aprenda a resolver desafios de Capture the Flag assistindo à gravação sob demanda do nosso workshop virtual introdutório.

Leia mais

blog feature snyk container custom base recommendations
Blog

Conheça o Evo Agentic AppSec: segurança que acompanha o ritmo do desenvolvimento com IA

O desenvolvimento impulsionado por IA está avançando mais rápido do que os fluxos de trabalho de segurança tradicionais. O AppSec agentivo da Snyk usa agentes de segurança para encontrar, corrigir e verificar vulnerabilidades continuamente, enquanto as pessoas definem os critérios.

blog feature ai violet
Blog

VulnBump: agentes de IA conseguem corrigir dependências vulneráveis sem quebrar o build?

O VulnBump testa agentes de IA em 307 atualizações reais de dependências. Uma correção só é considerada válida se eliminar a vulnerabilidade e mantiver o build e os testes funcionando.

Blog

Modelos de ponta encontraram as vulnerabilidades. Só o atacante encontrou as cadeias.

A análise estática encontrou as falhas, mas só os testes de ataque em aplicações ativas provaram como elas poderiam ser encadeadas para causar invasões. Uma comparação entre Evo COS, Claude Security e Claude Code Security.