Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Path Traversal no Node.js

Atualizado em: 8 de outubro de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

Path Traversal acontece quando uma aplicação usa entrada do usuário para montar caminhos e permite sair do diretório autorizado. Sequências como ../, caminhos absolutos, separadores alternativos e codificações podem expor arquivos, sobrescrever dados ou atingir configurações sensíveis.

Exemplo vulnerável

app.get('/files/:name', async (req, res) => {
  const filePath = path.join('/app/uploads', req.params.name);
  res.sendFile(filePath);
});

Um nome como ../../etc/passwd pode escapar, dependendo de normalização e uso posterior.

Use identificadores, não caminhos

A solução mais robusta é mapear um ID para um caminho armazenado pelo servidor:

const file = await files.findById(req.params.id);
if (!file || file.tenantId !== req.auth.tenantId) {
  throw new NotFoundError();
}

return streamPrivateFile(file.storageKey, res);

O cliente nunca escolhe diretório ou nome físico.

Verificação de containment

const ROOT = path.resolve('/app/public-files');

function resolveInsideRoot(input) {
  const candidate = path.resolve(ROOT, input);
  const relative = path.relative(ROOT, candidate);

  if (relative.startsWith('..') || path.isAbsolute(relative)) {
    throw new Error('Caminho fora da raiz');
  }

  return candidate;
}

Compare caminhos normalizados. Uma simples verificação com startsWith(ROOT) pode aceitar diretórios com prefixos semelhantes.

Allowlist de nomes

Quando o nome precisa vir do usuário, aceite um formato restrito:

if (!/^[a-zA-Z0-9_-]{1,80}\.pdf$/.test(filename)) {
  throw new ValidationError('Nome inválido');
}

Allowlist não substitui containment, mas reduz possibilidades.

Separadores de plataforma

Windows aceita barras invertidas e caminhos com drive. Teste em todas as plataformas de produção. Não substitua apenas ../ em string.

Codificação

Frameworks podem decodificar parâmetros automaticamente. Ataques usam percent-encoding simples ou duplo. Trabalhe com o valor final decodificado uma vez e não aplique múltiplas decodificações manuais.

Null bytes

Ambientes modernos tratam null bytes de forma mais segura, mas valide entrada e não dependa de extensão adicionada ao final para proteção.

Mesmo um caminho dentro da raiz pode apontar por symlink para fora. Em diretórios controlados por usuários, evite seguir symlinks, use flags seguras quando disponíveis e verifique o caminho real:

const realRoot = await fs.realpath(ROOT);
const realFile = await fs.realpath(candidate);
const relative = path.relative(realRoot, realFile);
if (relative.startsWith('..') || path.isAbsolute(relative)) throw new Error();

Existe risco de race condition entre verificação e abertura. Prefira armazenamento onde usuários não conseguem criar links.

TOCTOU

Time-of-check to time-of-use acontece quando o arquivo muda entre validar e abrir. Reduza a janela, use descritores, diretórios protegidos e APIs que operem sobre handles.

Uploads

Nunca use originalname como caminho. Gere UUID e mantenha extensão apenas se validada por conteúdo.

Extração de ZIP

Zip Slip é path traversal dentro de arquivos compactados:

for (const entry of archive.entries) {
  const output = resolveInsideRoot(entry.name);
  await extractEntry(entry, output);
}

Rejeite caminhos absolutos, .., links e tamanho descompactado excessivo.

sendFile

Alguns frameworks oferecem opção root:

res.sendFile(filename, { root: ROOT });

Mesmo assim, use nomes validados e revise o contrato da versão do framework.

Arquivos de template

Não permita que o usuário escolha um template arbitrário. Mapeie valores conhecidos:

const templates = {
  invoice: 'invoice.html',
  receipt: 'receipt.html',
};

const template = templates[req.query.type];
if (!template) throw new ValidationError();

Logs

Logs também podem sofrer path injection se o nome do arquivo vem do cliente. Use destino fixo e campos estruturados, não um arquivo por usuário.

Static files

Configure diretórios públicos explicitamente. Não sirva a raiz do projeto, uploads privados, arquivos .env, source maps ou repositório Git.

Permissões do processo

Execute como usuário sem privilégios. Mesmo que traversal ocorra, o processo não deve ler chaves, secrets ou arquivos do sistema.

Containers

Use filesystem somente leitura quando possível, monte volumes mínimos e não inclua segredos na imagem. Container não elimina traversal.

Respostas

Retorne 404 ou 400 sem revelar caminho real. Não devolva mensagens como “arquivo /etc/passwd não permitido”.

Testes

Teste:

  • ../ e variantes;
  • barras invertidas;
  • caminho absoluto;
  • percent-encoding;
  • dupla codificação;
  • symlink;
  • nome muito longo;
  • prefixos parecidos;
  • ZIP com entradas maliciosas.

Erros comuns

  • remover ../ por replace;
  • confiar em path.join sozinho;
  • usar startsWith simples;
  • ignorar Windows;
  • usar nome original de upload;
  • seguir symlinks;
  • servir a raiz do projeto;
  • processo com permissões amplas;
  • expor caminho nos erros.

Fluxo recomendado

  1. use IDs opacos;
  2. mapeie no servidor;
  3. normalize com resolve;
  4. verifique relative;
  5. aplique allowlist;
  6. controle symlinks;
  7. restrinja permissões;
  8. teste codificações;
  9. monitore tentativas.

Combine a proteção com Upload Seguro de Arquivos, Multipart, Tratamento de Erros e Audit Logs.

Consulte o guia de Path Traversal da OWASP e a documentação oficial de path.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita