Principais conclusões
- A Werkzeug oferece utilitários de segurança para o hash de senhas, a sanitização de nomes de arquivos e a junção segura de caminhos, além de um depurador protegido por PIN.
- Seu histórico de CVEs concentra-se no depurador, na análise de formulários com várias partes e na função safe_join() e a análise de cookies.
- O ramo 3.1.x é o único que tem garantia de correções de segurança do upstream, e a versão 3.1.6 ou posterior corrige todas as vulnerabilidades CVE abordadas aqui.
- ELS da TuxCare porta correções de segurança para o Werkzeug 1.0.1, 2.2.3 e 2.3.8, distribuídas via pip.
Um verificador de dependências sinaliza o Werkzeug como vulnerável, e ninguém da equipe se lembra de tê-lo instalado, pois o Flask o incorporou automaticamente. O Werkzeug é o kit de ferramentas subjacente ao Flask que lida com a infraestrutura de solicitações e respostas por trás de cada rota. Ele possui seu próprio histórico de CVEs e suas próprias configurações padrão, independentes do que o Flask adiciona sobre ele.
Este artigo aborda contra o que o Werkzeug oferece proteção, onde se encontram suas CVEs verificadas, como verificar sua própria exposição e como fortalecer uma aplicação do Werkzeug em produção.
O que é o Werkzeug e como ele se integra ao Flask
Werkzeug é uma biblioteca de utilitários WSGI mantida pelo projeto Pallets, o mesmo grupo responsável pelo Flask, Jinja e Click. Ela lida com os mecanismos de baixo nível de uma solicitação e resposta HTTP: análise de cabeçalhos, cookies e corpos de formulários multiparte, correspondência de rotas de URL e fornecimento de um depurador para desenvolvimento local.
O Flask foi desenvolvido com base no Werkzeug, em vez de substituí-lo. O Flask fornece decoradores de roteamento, modelos e estrutura de aplicação, mas seus objetos de solicitação e resposta são subclasses dos do Werkzeug, e grande parte da infraestrutura WSGI subjacente também provém do Werkzeug. Ao instalar o Flask, o Werkzeug é instalado como uma dependência; é por isso que a maioria das equipes o utiliza sem nunca adicioná-lo propositalmente a um arquivo de dependências.
Em produção, um servidor WSGI, como o Gunicorn, analisa o HTTP bruto e fornece ao Flask um ambiente WSGI. A partir daí, uma falha no tratamento de solicitações do Werkzeug fica acessível a qualquer pessoa capaz de enviar uma solicitação ao seu aplicativo. O roteamento ocorre antes que sua função de visualização seja executada, e a maioria dos demais dados da solicitação é analisada quando algo os lê. A análise de formulários, por exemplo, é acionada no momento em que o código acessa request.form ou request.files.
Proteções de segurança nativas do Werkzeug
O Werkzeug oferece vários auxiliares de segurança e mecanismos de proteção que uma aplicação Flask pode utilizar sem precisar adicionar outra dependência. A maioria deles só funciona se a aplicação realmente os chamar.
werkzeug.security fornece generate_password_hash() e check_password_hash(), que aplicam salt e hash às senhas usando um algoritmo configurável, em vez de deixar isso a cargo do código do aplicativo. secure_filename() remove separadores de caminho e outros caracteres inseguros de um nome de arquivo fornecido pelo usuário, de modo que o nome retornado seja muito mais seguro para ser salvo em um diretório controlado pelo aplicativo. No entanto, ela analisa apenas o nome. Ela não verifica o que já existe no disco e pode retornar uma string vazia; portanto, o código de upload ainda precisa lidar com ambas as situações.
safe_join() existe pelo mesmo motivo no nível do diretório. Ela se destina a combinar um diretório base confiável com um caminho fornecido pelo usuário e recusar-se a retornar qualquer coisa que saia desse diretório base, que é o que send_from_directory() utiliza para servir arquivos com segurança. O depurador interativo adiciona a exigência de um PIN antes de exibir um console Python ativo em um navegador, com o objetivo de impedir que um estranho que acesse acidentalmente um endpoint de depuração consiga executar código.
Nenhuma dessas proteções é completa por si só. Cada uma delas reduz um risco específico, e duas delas, safe_join() e o PIN do depurador, apresentaram falhas documentadas justamente no risco que foram criadas para eliminar. É daí que vem boa parte do histórico de CVEs abaixo.
Riscos de segurança e CVEs do Werkzeug
Até setembro de 2026, o registro público de CVEs da Werkzeug somava nove divulgações desde 2023. Vários dos CVEs mais recentes apresentam pontuações tanto no CVSS v3.1 quanto no CVSS v4.0, e os rastreadores nem sempre exibem ambas. Qual delas é mais alta depende do CVE. O safe_join() obtém pontuação mais alta na v4.0, enquanto o CVE-2024-49767 obtém pontuação mais alta na v3.1. Ver apenas uma delas pode fazer com que uma descoberta pareça mais ou menos urgente, mas a falha subjacente é idêntica.
| CVE | Gravidade (CVSS v3.1) | Versões afetadas | Corrigido em |
| CVE-2024-34069 | 7,5 Alto | < 3.0.3 | 3.0.3 |
| CVE-2023-46136 | 7,5 Alto (NVD) / 5,7 Moderado (GitHub) | 2.0.0rc1–2.3.7, 3.0.0 | 2.3.8, 3.0.1 |
| CVE-2023-25577 | 7,5 Alto | < 2.2.3 | 2.2.3 |
| CVE-2024-49767 | 7,5 Alto | 2.0.0rc1–3.0.5 | 3.0.6 |
| CVE-2024-49766 | 5.3 Médio | <= 3.0.5 (Windows, Python < 3.11) | 3.0.6 |
| CVE-2025-66221 | 5.3 Médio | < 3.1.4 | 3.1.4 |
| CVE-2026-21860 | 5.3 Médio | < 3.1.5 | 3.1.5 |
| CVE-2026-27199 | 5.3 Médio | <= 3.1.5 | 3.1.6 |
| CVE-2023-23934 | 3,5 Baixo (NVD) / 2,6 Baixo (GitHub) | < 2.2.3 | 2.2.3 |
Vulnerabilidade à execução remota de código no depurador
A vulnerabilidade do depurador, CVE-2024-34069, e seus mecanismos de exploração por CSRF são abordados em detalhes na análise da TuxCare sobre o que o fim do suporte ao Werkzeug significa para aplicativos Python. Resumindo, um depurador representa um risco de execução de código mesmo quando está restrito ao localhost e protegido por um PIN. O ataque requer um domínio e um subdomínio controlados pelo invasor, um desenvolvedor que interaja com eles e insira o PIN do depurador, além de uma URL adivinhada que acione o depurador. Se isso for bem-sucedido, o invasor acessa um depurador restrito ao localhost e executa código na máquina do desenvolvedor. A falha foi corrigida no upstream na versão 3.0.3, e nenhuma versão 2.x do upstream traz a correção, já que o suporte ao upstream 2.x terminou na versão 2.3.8. Para equipes que ainda usam a versão 2.x, o ELS da TuxCare oferece cobertura com uma correção retroportada para o Werkzeug 2.3.8, e o CVE Tracker lista a cobertura atual para todas as versões suportadas.
Negação de serviço por análise de dados em várias partes
Três CVEs distintos têm origem no mesmo caminho de código. O CVE-2023-25577 permitia que uma solicitação contivesse um número ilimitado de partes multipart, de modo que o custo de análise aumentava proporcionalmente ao número de partes controladas pelo invasor, até que a CPU ou a memória se esgotassem. A falha foi corrigida na versão 2.2.3, que também foi a versão que introduziu o limite de max_form_parts .
A vulnerabilidade CVE-2023-46136 atacou o mesmo analisador de um ângulo diferente, com uma única parte criada propositalmente. Um arquivo que começa com um caractere CR ou LF e é seguido por megabytes de dados que não contêm mais nenhum caractere CR ou LF força o analisador a continuar verificando repetidamente um buffer cada vez maior em busca do limite. O NVD classifica essa vulnerabilidade como 7,5 (Alta), enquanto o aviso do GitHub a classifica como 5,7 (Moderada). Alguns rastreadores mostram um terceiro número, 8,0 (Alto), que se origina da pontuação da CNA no registro oficial do CVE, e não do aviso atual do GitHub. A análise completa dessa divisão está no artigo sobre o fim da vida útil do Werkzeug.
A vulnerabilidade CVE-2024-49767 corrigiu uma falha diferente no mesmo analisador. Um corpo de formulário malicioso poderia contornar max_form_memory_size por completo, de modo que o limite de memória definido por um aplicativo nunca era efetivamente aplicado. O NVD classifica essa vulnerabilidade com 7,5 (Alta) (CVSS v3.1), e o comunicado do GitHub a classifica com 6,9 (Moderada) (CVSS v4.0).
Um limite de analisador só é útil se for aplicado com antecedência suficiente para impedir o uso de memória controlada pelo invasor, e o limite adequado depende do tipo de ataque. max_form_parts limita enxurradas de partes minúsculas, como as por trás da vulnerabilidade CVE-2023-25577. max_content_length e um limite para o tamanho do corpo na proxy reversa restringem o tamanho total da solicitação, e nenhum desses depende de o analisador multipart detectar o problema internamente.
Falhas no caminho do Windows no safe_join
safe_join() recebeu quatro correções em cerca de dezesseis meses; todas as correções foram específicas do Windows e todas diziam respeito à forma como a função determina se um caminho é seguro.
A CVE-2024-49766 é a mais antiga das quatro. Nas versões do Python anteriores à 3.11 em execução no Windows, os.path.isabs() não reconhece caminhos UNC, como //server/share como absolutos, e safe_join() depende dessa verificação; portanto, um caminho UNC poderia passar despercebido. Isso foi corrigido na versão 3.0.6.
Os outros três envolvem nomes de dispositivos reservados do Windows, como CON, AUX e NUL, que o Windows trata como se estivessem presentes e fossem legíveis em todos os diretórios, independentemente do que o diretório realmente contenha. O CVE-2025-66221 constatou que safe_join() aceitava esses nomes sem questionar, e a versão 3.1.4 corrigiu isso. O CVE-2026-21860 constatou que a primeira correção não havia considerado extensões compostas, como CON.txt.html, nomes com espaços à direita e alguns nomes especiais adicionais, o que foi corrigido na versão 3.1.5. Em seguida, o CVE-2026-27199 mostrou que um caminho com vários segmentos, como example/NUL ainda era aceito, e a versão 3.1.6 corrigiu essa falha.
Em cada um desses três casos de nomes de dispositivos, a função `send_from_directory()` abre o dispositivo com sucesso e, em seguida, fica travado indefinidamente na leitura, ocupando um trabalhador por solicitação até que o processo fique sem trabalhadores para atender ao tráfego real.
Vulnerabilidade na análise de cookies
O CVE-2023-23934 é o item mais discreto desta lista. O NVD atribui a ele uma pontuação de 3,5 (Baixa), e o GitHub, a CNA que atribuiu o CVE, atribui uma pontuação de 2,6 (Baixa). Alguns navegadores aceitam um cookie com o formato de =valor sem nome, e as versões do Werkzeug anteriores à 2.2.3 analisavam um cookie como =__Host-test=bad como um __Host-test .
Assim, um subdomínio malicioso ou comprometido poderia inserir esse cookie malformado e fazer com que o Werkzeug lesse um valor controlado pelo invasor sob o parâmetro protegido __Host-test , o que anula todo o propósito do __Host- . Isso foi corrigido na versão 2.2.3.
Ambas as pontuações são baixas porque o ataque requer tanto um navegador vulnerável quanto um subdomínio adjacente malicioso ou comprometido. A consequência é outra história se o cookie falsificado contiver um token de sessão ou um token CSRF. O CVSS avalia o mecanismo e não tem como saber o que um determinado aplicativo armazena nesse cookie.
Como verificar se a sua versão do Werkzeug apresenta vulnerabilidades
A verificação da exposição leva cerca de dois minutos por serviço. O objetivo é responder a uma pergunta: a versão do Werkzeug está realmente em execução na produção em um branch que ainda recebe correções?
Comece pelo que está instalado, e não pelo que consta no arquivo de requisitos.
pip show ferramenta
Execute isso dentro do contêiner ou ambiente virtual que está efetivamente atendendo ao tráfego. Qualquer versão anterior à 3.1 fica fora do único ramo que garante correções de segurança do upstream. A Pallets pode fazer o backport de uma correção para o ramo de recursos anterior, mediante solicitação, mas isso não é garantido. Uma versão 3.1.x anterior à 3.1.6 está no ramo correto, mas ainda carece de pelo menos uma correção, e a versão atual no momento da redação deste texto é a 3.1.8. De qualquer forma, as colunas “Versões afetadas” e “Corrigido em” na tabela acima mostram se cada CVE se aplica e qual versão contém sua correção do upstream, e a função exclusiva para Windows safe_join() só se aplicam a hosts do Windows.
Os números de versão, por si só, não determinam quais CVEs se aplicam a uma determinada versão do Python e a um determinado sistema operacional; portanto, execute uma ferramenta de auditoria nesse mesmo ambiente.
pip install pip-audit
pip-audit
Sem argumentos, o pip-audit verifica os pacotes instalados no ambiente ativo. Se for necessário auditar um arquivo de requisitos, o `pip-audit -r requirements.txt` cuida disso. O pip-audit é mantido pela Python Packaging Authority e, por padrão, obtém dados de vulnerabilidades do PyPI, com o OSV disponível como fonte alternativa por meio do -s osv. Algumas equipes também executam um segundo scanner, como o Safety, para obter outra perspectiva dos dados dos alertas.
A atualização do pacote no disco não substitui o código do Werkzeug que um worker em execução já tenha importado. Workers do Gunicorn ou do uWSGI com execução prolongada mantêm o código antigo carregado até que sejam reiniciados; portanto, reinicie os workers após qualquer alteração no Werkzeug e confirme se a nova versão foi realmente carregada, e não apenas instalada.
No que diz respeito especificamente às notícias de segurança do Werkzeug, o CVE Tracker da TuxCare lista as vulnerabilidades corrigidas pelo ELS à medida que são confirmadas em versões mais antigas que não recebem mais correções do desenvolvedor original.
Melhores práticas para proteger aplicativos Werkzeug
Parte do risco do Werkzeug decorre da configuração, e não da versão, e esses hábitos se aplicam a todas as versões.
Comece com o depurador. Defina debug=False e nunca habilite o depurador interativo em ambiente de produção, mesmo que esteja vinculado a 127.0.0.1, já que a vulnerabilidade CVE-2024-34069 demonstrou que o localhost não é o limite seguro que parece ser. Execute por meio de um servidor WSGI de produção, como Gunicorn, uWSGI ou Waitress, pois run_simple() e flask run são ferramentas de desenvolvimento que nunca foram projetadas para lidar com tráfego de produção.
Em seguida, defina limites de solicitações de forma deliberada. max_content_length, max_form_memory_sizee max_form_parts devem corresponder ao que o aplicativo realmente aceita, e o tamanho do corpo da solicitação também deve ser limitado no proxy reverso, para que solicitações multipart excessivamente grandes ou complexas sejam rejeitadas antes que possam consumir muita memória ou CPU. Valores explícitos são mais importantes nas versões anteriores à 3.1, nas quais max_form_memory_size era, por padrão, ilimitado. O Werkzeug 3.1 alterou esse padrão para 500 kB.
Sempre que possível, sirva arquivos estáticos a partir de um servidor web dedicado, como o nginx, o que mantém send_from_directory() e safe_join() fiquem totalmente fora desse caminho de solicitação. Além disso, execute o processo do aplicativo como um usuário que não seja root, para que um bug de caminho que consiga passar tenha menos acesso.
A configuração corrige vulnerabilidades que uma atualização de versão não resolve por si só, e o contrário também é verdadeiro. Nenhuma dessas configurações substitui a execução de uma versão atual da série 3.1.x, e todas as vulnerabilidades (CVE) da tabela acima foram corrigidas na versão 3.1.6 e nas versões posteriores.
Como o TuxCare ELS mantém o Werkzeug seguro após o fim da vida útil
Nem sempre é possível realizar uma atualização dentro de um determinado prazo. Uma versão fornecida pelo fornecedor, uma implantação certificada ou um ambiente de execução do Python cuja migração não foi prevista no orçamento podem manter um aplicativo preso a uma versão antiga do Werkzeug muito tempo depois que as vulnerabilidades (CVEs) listadas naquela tabela passarem a afetá-lo.
Suporte de ciclo de vida ilimitado da TuxCare para o Werkzeug continua fornecendo correções de segurança para as versões 1.0.1, 2.2.3 e 2.3.8, disponibilizadas como pacotes assinados por meio do pip. As equipes podem permanecer na versão que já utilizam enquanto as correções de segurança continuam sendo fornecidas; assim, um novo CVE não se transforma automaticamente em uma migração de emergência, e a migração propriamente dita pode ocorrer de acordo com o cronograma de cada equipe.
Perguntas frequentes sobre a segurança do Werkzeug
Como posso verificar se a minha versão do Werkzeug apresenta vulnerabilidades conhecidas?
Execute pip show werkzeug no ambiente implantado para confirmar a versão instalada e, em seguida, execute pip-audit nesse mesmo ambiente para verificar se os pacotes estão em conformidade com os alertas conhecidos. Para os CVEs abordados aqui, a versão 3.1.6 é a primeira versão do desenvolvedor original que inclui todas as correções, embora a versão 3.1.8 seja a versão atual no momento da redação deste artigo e a melhor opção.
A atualização do Flask também corrige as vulnerabilidades (CVEs) do Werkzeug?
Não necessariamente. O Flask define uma versão mínima do Werkzeug, em vez de um limite máximo, e o comando `pip install –upgrade flask` não altera o Werkzeug já instalado, desde que ele ainda atenda a esse mínimo. Vale a pena atualizar o Flask, mas verificar diretamente a versão do Werkzeug instalada é a única maneira de ter certeza.
Qual é a maneira mais segura de executar o Werkzeug em ambiente de produção?
Execute-o em um servidor WSGI dedicado, como o Gunicorn, o uWSGI ou o Waitress, com debug=False, e max_content_length, max_form_memory_sizee max_form_parts , além de arquivos estáticos servidos por um servidor web separado, em vez de send_from_directory().
Por que algumas vulnerabilidades (CVEs) do Werkzeug apresentam pontuações de gravidade diferentes em locais distintos?
Várias vulnerabilidades CVE recentes do Werkzeug apresentam pontuações tanto no CVSS v3.1 quanto no CVSS v4.0, e os dois modelos atribuem pesos diferentes aos mesmos detalhes. A CVE-2026-27199, uma das correção do safe_join() , apresenta uma pontuação de 5,3 no CVSS v3.1 no NVD e de 6,3 no CVSS v4.0 no comunicado do GitHub; portanto, o número exibido por um rastreador depende da fonte e do padrão que ele utiliza por padrão. A vulnerabilidade se comporta da mesma forma em ambos os casos.

