Conteúdos

pip-audit: verificando vulnerabilidades nas dependências Python

Como programo em Python, uma das minhas preocupações é a segurança das aplicações que desenvolvo. Tento trabalhar seguindo as melhores práticas: valido entradas, cuido das permissões, evito expor informações sensíveis e procuro manter a infraestrutura organizada.

Mesmo assim, existe uma parte importante do projeto que não foi escrita por mim: as dependências.

Uma aplicação pode ter um código bem revisado e ainda carregar uma vulnerabilidade por meio de um pacote instalado pelo pip. Muitas vezes a dependência direta também traz outras dependências, que ficam ainda mais escondidas no ambiente. É fácil esquecer que cada pacote adicionado ao projeto aumenta a superfície que precisa ser acompanhada.

Foi para ajudar nesse problema que comecei a olhar para o pip-audit. No rascunho, eu costumava chamar a ferramenta de “py-audit”, mas o nome correto do projeto é pip-audit. Ele verifica ambientes Python, arquivos de requisitos e árvores de dependências em busca de vulnerabilidades conhecidas.

O que o pip-audit verifica

O pip-audit consulta fontes de avisos de segurança para relacionar os pacotes encontrados no ambiente com vulnerabilidades já registradas. Quando encontra um problema, informa o pacote, a versão instalada, o identificador da vulnerabilidade e, quando existe, a versão que corrige o problema.

Isso é diferente de simplesmente perguntar se existe uma versão mais nova de cada pacote. Uma atualização pode conter melhorias, correções de bugs ou novos recursos sem ser uma correção de segurança. O objetivo principal do pip-audit é encontrar vulnerabilidades conhecidas, não substituir o gerenciamento normal das dependências.

A ferramenta também ajuda a enxergar dependências transitivas. Eu posso ter instalado apenas um pacote diretamente, mas ele pode depender de vários outros. Se uma dessas dependências indiretas tiver uma vulnerabilidade conhecida, ela também merece atenção.

Instalação

A instalação é simples. Dentro do ambiente Python que quero utilizar, executo:

python -m pip install pip-audit

Também posso instalar a ferramenta de forma isolada com pipx, evitando misturar o programa com as dependências da aplicação:

pipx install pip-audit

Eu prefiro executar a auditoria em um ambiente virtual ou diretamente sobre as dependências declaradas do projeto. Assim, o resultado representa melhor o que será instalado e executado pela aplicação.

Auditando o ambiente atual

Quando já estou dentro do ambiente virtual do projeto, posso executar:

pip-audit

Se não houver vulnerabilidades conhecidas, a ferramenta informa que não encontrou problemas. Quando encontra alguma, mostra os pacotes afetados e as versões de correção disponíveis.

Esse resultado não deve ser tratado como um simples “passou” ou “falhou”. Eu preciso entender qual pacote está vulnerável, se ele é utilizado no caminho que me preocupa, qual versão corrige o problema e se a atualização pode causar alguma incompatibilidade.

Verificando um arquivo de requisitos

Um fluxo comum em projetos Python é manter as dependências em requirements.txt. Nesse caso, posso auditar o arquivo diretamente:

pip-audit -r requirements.txt

Se o arquivo estiver totalmente fixado em versões específicas, a análise fica mais previsível. Em projetos que usam arquivos de lock, também é importante conferir se a ferramenta e a versão instalada suportam o formato adotado pelo projeto.

Depois de atualizar uma dependência, eu gero novamente o arquivo ou atualizo o lockfile conforme o gerenciador utilizado. Não adianta corrigir apenas o ambiente local e deixar a versão vulnerável declarada no repositório, porque o próximo deploy poderá reinstalar o problema.

Auditando o projeto

Também posso apontar para o diretório do projeto:

pip-audit .

Esse modo é útil quando o projeto possui seus metadados em um pyproject.toml ou em outro formato reconhecido. A vantagem é deixar a auditoria próxima da forma como o projeto organiza suas dependências, em vez de depender apenas de uma instalação manual feita na minha máquina.

Para guardar um relatório que possa ser analisado depois, posso gerar uma saída em JSON:

pip-audit -r requirements.txt --format=json --output=pip-audit.json

O relatório pode ser arquivado junto aos artefatos do CI, encaminhado para uma ferramenta de análise ou usado por outro processo de automação. Só tomo cuidado para não incluir nesse arquivo URLs de repositórios privados com tokens ou outras informações sensíveis.

Quando aparece uma vulnerabilidade

Encontrar uma vulnerabilidade é o início do trabalho, não o fim. Primeiro verifico o identificador informado e leio o aviso correspondente. Depois avalio:

  • Qual pacote e qual versão estão afetados.
  • Se existe uma versão corrigida.
  • Se o pacote é uma dependência direta ou transitiva.
  • Se a vulnerabilidade alcança o modo como a aplicação utiliza o pacote.
  • Quais mudanças incompatíveis podem surgir com a atualização.
  • Quais testes precisam ser executados antes do deploy.

Quando há uma versão corrigida, atualizo a dependência em uma branch separada, executo os testes e reviso o diff do lockfile ou do arquivo de requisitos. Só depois levo a alteração para o ambiente de produção.

O pip-audit possui a opção --fix, que pode tentar atualizar dependências vulneráveis automaticamente:

pip-audit --fix

Eu vejo essa opção como um auxílio, não como uma substituição do processo de atualização. Uma mudança automática pode resolver o aviso e, ao mesmo tempo, introduzir uma incompatibilidade. Por isso, é melhor executar em uma branch, manter o estado anterior recuperável e rodar a suíte de testes antes de aceitar a alteração.

Também pode acontecer de não existir uma versão corrigida. Nesse caso, preciso avaliar uma alternativa: substituir o pacote, reduzir a exposição, aplicar uma mitigação, aceitar temporariamente o risco com uma justificativa ou acompanhar o projeto até surgir uma correção. Ignorar o alerta sem registrar a decisão não melhora a segurança.

Incluindo no desenvolvimento

A auditoria pode fazer parte da rotina local. Antes de abrir um pull request, executo o comando sobre o ambiente ou sobre o arquivo de requisitos. Assim, tenho a chance de descobrir o problema enquanto a alteração ainda está fresca e é simples de revisar.

Um exemplo de etapa em um pipeline de integração contínua é:

- name: Auditar dependências Python
  run: pip-audit -r requirements.txt

Se o pip-audit encontrar vulnerabilidades, ele retorna um código de saída diferente de zero. O pipeline pode então bloquear o build, abrir um alerta ou apenas registrar o relatório, dependendo da política do projeto.

Eu não usaria necessariamente o mesmo critério para todos os ambientes. Uma vulnerabilidade sem versão de correção pode exigir um tratamento diferente de uma falha crítica com correção disponível. O importante é definir a regra antes do alerta aparecer, para não tomar uma decisão diferente a cada execução.

Também é possível produzir saídas em Markdown, JSON ou formatos de SBOM. Isso facilita integrar os resultados ao processo de revisão e acompanhar a evolução das dependências ao longo do tempo.

O que a ferramenta não resolve

O pip-audit é uma camada específica de segurança. Ele não substitui uma revisão de código, um teste de invasão, uma análise de configuração ou a atualização do sistema operacional.

Ele também não é um analisador estático do meu código. Não vai descobrir uma autorização mal implementada, uma senha exposta em um arquivo ou uma falha de validação que escrevi na aplicação. Da mesma forma, o fato de uma dependência não aparecer com alerta não prova que ela é segura em todos os sentidos.

O foco está nas vulnerabilidades conhecidas dos pacotes Python identificados. A ferramenta não deve ser usada como garantia de que um pacote desconhecido ou malicioso é confiável, nem como substituta da avaliação da origem das dependências. Eu ainda preciso escolher pacotes mantidos, revisar mudanças relevantes e controlar de onde eles são instalados.

Minha conclusão

Quando comecei a programar em Python, minha preocupação com segurança ficava muito concentrada no código que eu escrevia. Com o tempo, percebi que as dependências fazem parte da aplicação e também precisam ser tratadas como código de terceiros sob responsabilidade do projeto.

O pip-audit ajuda a tornar essa preocupação mais prática. Ele verifica os pacotes instalados e as dependências declaradas, relaciona versões vulneráveis com avisos conhecidos e mostra quando existe uma atualização de segurança para ser avaliada.

Isso ajuda tanto no desenvolvimento quanto na infraestrutura. No desenvolvimento, posso encontrar o problema antes de publicar uma nova versão. Na infraestrutura, consigo auditar o ambiente que será instalado e evitar que uma imagem ou servidor seja criado com pacotes que já possuem vulnerabilidades conhecidas.

Não existe uma ferramenta única que resolva toda a segurança de uma aplicação. Ainda preciso revisar o código, proteger os segredos, atualizar o sistema e entender o contexto de cada alerta. Mas incluir uma auditoria repetível das dependências é uma prática simples que reduz uma classe importante de riscos.

O repositório oficial do pip-audit e sua página no PyPI apresentam os detalhes e as opções disponíveis. Para mim, ele se tornou mais uma etapa pequena, mas útil, para tentar manter meus projetos Python e a infraestrutura que os executa mais seguros.