Path traversal: prevenindo acesso a arquivos não autorizados
1. O que é Path Traversal e por que é perigoso?
Path Traversal (ou Directory Traversal) é um tipo de ataque cibernético onde o invasor manipula parâmetros de entrada que definem caminhos de arquivos para acessar diretórios e arquivos restritos no servidor. A técnica explora a falta de validação adequada de entrada do usuário, permitindo que o atacante "escape" do diretório base da aplicação.
O exemplo clássico utiliza sequências de ../ para navegar pela estrutura de diretórios:
GET /files?path=../../../etc/passwd HTTP/1.1
Host: exemplo.com
Em sistemas Windows, as variações podem incluir ..\ ou combinações de encoding como %2e%2e%2f (URL encoding de ../) ou ..%252f (double encoding).
O impacto de um Path Traversal bem-sucedido pode ser devastador:
- Vazamento de credenciais: acesso a arquivos como /etc/passwd ou config.php com senhas de banco de dados
- Exposição de código-fonte: revelação de lógica de negócios e vulnerabilidades adicionais
- Execução remota de comandos: quando combinado com outras falhas (ex: upload de arquivos maliciosos)
- Acesso a dados sensíveis: arquivos de log, backups, arquivos temporários
2. Como identificar vulnerabilidades de Path Traversal no código
Os padrões mais comuns de entrada do usuário que podem ser explorados incluem:
- Parâmetros de URL:
?file=relatorio.pdfou?path=/uploads/ - Cabeçalhos HTTP:
Referer,X-Forwarded-For - Uploads de arquivos: nomes de arquivo com caminhos relativos
- APIs RESTful: endpoints que recebem caminhos de arquivos como parâmetros
As falhas típicas no código são:
// VULNERÁVEL: concatenação direta
$filename = $_GET['file'];
$content = file_get_contents('/var/www/files/' . $filename);
// VULNERÁVEL: falta de normalização
string path = Path.Combine(basePath, userInput);
File.ReadAllText(path);
// VULNERÁVEL: uso de funções perigosas
import os
filename = request.args.get('file')
with open(f'/app/data/{filename}', 'r') as f:
data = f.read()
Para identificar essas vulnerabilidades, utilize:
- Revisão manual de código: procure por operações de arquivo usando entrada não validada
- Testes de penetração: envie payloads como ../../../etc/passwd, ....//....//etc/passwd, ..%252f..%252f..%252fetc%252fpasswd
- Ferramentas automatizadas: scanners como OWASP ZAP, Burp Suite, ou linters de segurança
3. Mecanismos de prevenção: validação de entrada
A validação de entrada deve seguir o princípio de whitelist (lista de permitidos) em vez de blacklist, pois blacklists são facilmente contornáveis com variações de encoding.
Exemplo de whitelist de caracteres:
// PHP - Whitelist de caracteres permitidos
$allowedChars = '/^[a-zA-Z0-9_\-\.]+$/';
if (!preg_match($allowedChars, $filename)) {
die("Nome de arquivo inválido");
}
Validação de extensões contra conjunto fixo:
// Python - Validação de extensões
ALLOWED_EXTENSIONS = {'.pdf', '.txt', '.jpg', '.png'}
filename = request.args.get('file')
if not any(filename.endswith(ext) for ext in ALLOWED_EXTENSIONS):
abort(400, "Extensão não permitida")
Rejeição de caracteres suspeitos:
// C# - Rejeição de caracteres perigosos
string[] dangerousPatterns = { "..", "/", "\\", "%00", ":", "*", "?" };
foreach (var pattern in dangerousPatterns) {
if (userInput.Contains(pattern)) {
throw new SecurityException("Entrada inválida");
}
}
4. Normalização e saneamento de caminhos
Após a validação inicial, é crucial normalizar o caminho para garantir que ele não aponte para fora do diretório permitido.
Exemplo em PHP com realpath():
// PHP - Normalização segura
$basePath = '/var/www/files/';
$userPath = $_GET['file'];
// Concatena e normaliza
$fullPath = realpath($basePath . $userPath);
$baseReal = realpath($basePath);
// Verifica se o caminho normalizado começa com o diretório base
if (strpos($fullPath, $baseReal) !== 0) {
die("Acesso negado");
}
Exemplo em Python com os.path.realpath():
# Python - Verificação de diretório base
import os
BASE_DIR = '/var/www/files/'
user_input = request.args.get('file')
full_path = os.path.realpath(os.path.join(BASE_DIR, user_input))
if not full_path.startswith(os.path.realpath(BASE_DIR)):
abort(403, "Acesso negado")
Exemplo em C# com Path.GetFullPath():
// C# - Verificação de diretório base
string basePath = @"C:\app\files\";
string userInput = Request.QueryString["file"];
string fullPath = Path.GetFullPath(Path.Combine(basePath, userInput));
if (!fullPath.StartsWith(Path.GetFullPath(basePath))) {
throw new UnauthorizedAccessException("Acesso negado");
}
5. Boas práticas de armazenamento e acesso a arquivos
A abordagem mais segura é nunca expor caminhos reais de arquivos ao cliente. Em vez disso, utilize identificadores seguros:
Mapeamento de IDs para arquivos:
# Tabela no banco de dados
# id | filename | path
# 1 | relatorio.pdf | /var/storage/files/abc123.pdf
# 2 | foto_perfil.jpg | /var/storage/images/def456.jpg
# Código seguro
file_id = request.args.get('id')
file_record = db.query("SELECT path FROM files WHERE id = ?", file_id)
if file_record:
serve_file(file_record.path)
else:
abort(404)
Armazenamento fora da raiz pública:
# Configuração do servidor web (Nginx)
location /files/ {
internal; # Bloqueia acesso direto
alias /var/storage/files/;
}
# Apenas o backend pode servir arquivos via script PHP/Python
Nunca exponha caminhos reais ao cliente:
# ERRADO: retornar caminho real
GET /download?file=/var/www/uploads/relatorio.pdf
# CERTO: usar token ou ID
GET /download?id=12345&token=abc123def456
6. Configuração do ambiente e hardening do servidor
Medidas de hardening reduzem o impacto mesmo se uma vulnerabilidade existir:
Desabilitação de listagem de diretórios:
# Apache
Options -Indexes
# Nginx
autoindex off;
Permissões mínimas (princípio do menor privilégio):
# O usuário do servidor web deve ter acesso apenas ao necessário
chown -R www-data:www-data /var/www/app
chmod -R 755 /var/www/app
chmod 644 /var/www/app/config.php # Apenas leitura
chmod 700 /var/www/app/keys/ # Restrito
Isolamento com chroot jails ou contêineres:
# Docker: isolar o serviço em um contêiner
FROM python:3.9-slim
WORKDIR /app
COPY app.py .
RUN useradd -m appuser
USER appuser
CMD ["python", "app.py"]
7. Testes e monitoramento contínuo
Casos de teste automatizados:
# Teste unitário (Python)
def test_path_traversal_prevention():
app = create_app()
with app.test_client() as client:
# Teste com path traversal
resp = client.get('/files?path=../../../etc/passwd')
assert resp.status_code == 403
# Teste com double encoding
resp = client.get('/files?path=%2e%2e%2f%2e%2e%2fetc/passwd')
assert resp.status_code == 403
# Teste com caminho válido
resp = client.get('/files?path=relatorio.pdf')
assert resp.status_code == 200
Logging e alertas para tentativas suspeitas:
# Python - Logging de tentativas suspeitas
import logging
def serve_file(filename):
if '..' in filename or '/' in filename:
logging.warning(f"Tentativa de path traversal: {filename}")
# Alertar equipe de segurança
send_alert(f"Path traversal detectado: {filename} de IP {request.remote_addr}")
abort(403)
Revisão periódica de dependências:
# Verificar bibliotecas de manipulação de arquivos
pip audit # Python
npm audit # Node.js
composer audit # PHP
Referências
- OWASP - Path Traversal — Guia completo da OWASP sobre o ataque de Path Traversal, incluindo exemplos de payloads e técnicas de prevenção
- CWE-22: Improper Limitation of a Pathname to a Restricted Directory — Definição oficial da MITRE sobre a vulnerabilidade CWE-22, com descrição técnica e exemplos
- PortSwigger - Directory Traversal — Tutorial interativo da PortSwigger (criadores do Burp Suite) com laboratórios práticos de Path Traversal
- PHP Manual - realpath() — Documentação oficial da função
realpath()do PHP, essencial para normalização segura de caminhos - NIST - Path Traversal Prevention Cheat Sheet — Cheat sheet da OWASP para prevenção de vulnerabilidades em upload de arquivos e path traversal
- Microsoft - Path.GetFullPath Method — Documentação oficial da Microsoft sobre normalização de caminhos em .NET/C#
- Python Documentation - os.path.realpath() — Documentação oficial da função
os.path.realpath()do Python para resolução de caminhos absolutos