Upload de arquivos: validação, restrições e armazenamento seguro
1. Introdução aos Riscos de Upload de Arquivos
1.1. Por que upload de arquivos é um vetor crítico de ataque
Funcionalidades de upload de arquivos estão presentes em praticamente toda aplicação web moderna — perfis de usuário, sistemas de anexos, galerias de imagens, portais de documentos. Essa superfície de ataque é particularmente perigosa porque permite que o atacante insira conteúdo arbitrário no servidor. Um único arquivo malicioso pode comprometer toda a infraestrutura.
1.2. Principais ameaças
As três ameaças mais comuns associadas ao upload de arquivos são:
- Execução remota de código (RCE): arquivos como PHP, ASP, JSP ou scripts shell podem ser interpretados pelo servidor web se armazenados em diretórios acessíveis.
- Distribuição de malware: o servidor se torna um hospedeiro de arquivos maliciosos que infectam outros usuários.
- Negação de serviço (DoS): upload de arquivos muito grandes, em grande quantidade ou com conteúdo compressível (zip bombs) pode exaurir recursos do servidor.
1.3. Visão geral das camadas de defesa
A segurança no upload de arquivos deve ser implementada em três camadas:
- Entrada: validação rigorosa antes de qualquer processamento.
- Processamento: sanitização, verificação de conteúdo e transformação segura.
- Saída: armazenamento isolado e distribuição controlada.
2. Validação Rigorosa do Lado do Servidor
2.1. Verificação de tipo MIME real
Nunca confie na extensão do arquivo ou no cabeçalho Content-Type enviado pelo cliente. Ambos podem ser facilmente falsificados. Utilize bibliotecas que leem os primeiros bytes do arquivo (magic bytes) para determinar o tipo real.
# Exemplo em Python usando python-magic
import magic
def verificar_tipo_real(caminho_arquivo):
mime = magic.from_file(caminho_arquivo, mime=True)
tipos_permitidos = ['image/jpeg', 'image/png', 'application/pdf']
if mime not in tipos_permitidos:
raise ValueError(f"Tipo {mime} não permitido")
return mime
2.2. Validação de extensão contra lista branca
Sempre utilize uma whitelist de extensões explicitamente permitidas. Nunca use blacklists, pois são fáceis de contornar com extensões duplas ou variações.
# Validação de extensão com whitelist
import os
EXTENSOES_PERMITIDAS = {'.jpg', '.jpeg', '.png', '.gif', '.pdf'}
def validar_extensao(nome_arquivo):
_, extensao = os.path.splitext(nome_arquivo.lower())
if extensao not in EXTENSOES_PERMITIDAS:
raise ValueError(f"Extensão {extensao} não permitida")
2.3. Limitação de tamanho e verificação de integridade
Defina limites máximos de tamanho e verifique se o arquivo não foi corrompido ou truncado durante o upload.
# Validação de tamanho máximo (5 MB)
TAMANHO_MAXIMO = 5 * 1024 * 1024 # 5 MB
def validar_tamanho(arquivo):
arquivo.seek(0, os.SEEK_END)
tamanho = arquivo.tell()
arquivo.seek(0)
if tamanho > TAMANHO_MAXIMO:
raise ValueError(f"Arquivo excede {TAMANHO_MAXIMO} bytes")
if tamanho == 0:
raise ValueError("Arquivo vazio")
3. Sanitização e Processamento Seguro do Conteúdo
3.1. Reprocessamento de imagens
Imagens podem conter metadados maliciosos, scripts em comentários EXIF ou payloads embutidos em pixels. A melhor prática é reprocessar toda imagem recebida: redimensionar, recompressar e salvar em formato padronizado.
# Reprocessamento de imagem com Pillow
from PIL import Image
def sanitizar_imagem(caminho_entrada, caminho_saida):
imagem = Image.open(caminho_entrada)
# Redimensionar para tamanho máximo seguro
imagem.thumbnail((1200, 1200))
# Salvar em formato padronizado, sem metadados
imagem.save(caminho_saida, 'JPEG', quality=85, optimize=True)
3.2. Análise com antivírus
Integre um scanner antivírus para detectar malware antes de armazenar o arquivo. Soluções como ClamAV podem ser acionadas via linha de comando.
# Verificação com ClamAV
import subprocess
def verificar_antivirus(caminho_arquivo):
resultado = subprocess.run(
['clamscan', '--stdout', caminho_arquivo],
capture_output=True, text=True
)
if 'FOUND' in resultado.stdout:
raise ValueError("Malware detectado no arquivo")
3.3. Rejeição de poliglotas e scripts embutidos
Arquivos poliglotas combinam múltiplos formatos (ex.: GIF que também é PHP). Utilize verificações de integridade estrutural do formato declarado.
4. Restrições de Nomenclatura e Metadados
4.1. Geração de nomes únicos
Nunca utilize o nome original do arquivo para armazenamento. Gere nomes aleatórios com UUID ou hash do conteúdo.
import uuid
import hashlib
def gerar_nome_seguro(arquivo):
# Usar hash do conteúdo para evitar duplicatas
hash_conteudo = hashlib.sha256(arquivo.read()).hexdigest()
arquivo.seek(0)
return f"{hash_conteudo}_{uuid.uuid4().hex}"
4.2. Sanitização contra path traversal
Mesmo que o nome original não seja usado para armazenamento, sanitize-o para logging e exibição segura.
import re
def sanitizar_nome_original(nome):
# Remove caracteres de path traversal e não alfanuméricos
nome_limpo = re.sub(r'[^\w\.\-]', '_', nome)
return nome_limpo[:100] # Limita tamanho
4.3. Remoção de metadados
Metadados EXIF podem conter geolocalização, dados do dispositivo e strings injetadas. Remova-os completamente.
5. Armazenamento Seguro dos Arquivos
5.1. Armazenamento fora da raiz web
Nunca armazene uploads em diretórios servidos diretamente pelo servidor web (ex.: public/uploads). Use diretórios fora da raiz ou serviços de armazenamento externos.
# Estrutura de diretórios segura
/projeto
/app
/uploads # Fora da raiz web
/public
index.php # Raiz web
5.2. Permissões restritivas e isolamento
Configure permissões mínimas: o diretório de uploads deve ter permissão de escrita apenas para o processo da aplicação, e nunca permissão de execução.
# Configuração de permissões no Linux
chmod 750 /projeto/app/uploads
chown www-data:www-data /projeto/app/uploads
5.3. Servir arquivos via proxy de download
Nunca forneça URLs diretas para os arquivos. Crie um script intermediário que verifica autenticação, autorização e aplica cabeçalhos de segurança.
# Exemplo de endpoint de download proxy (Flask)
@app.route('/download/<file_id>')
def download_arquivo(file_id):
if not usuario_autorizado():
abort(403)
caminho = obter_caminho_seguro(file_id)
return send_file(
caminho,
as_attachment=True,
download_name='documento.pdf'
)
6. Controle de Acesso e Autenticação
6.1. Autenticação obrigatória
Todo upload deve exigir autenticação. Verifique tokens de sessão, JWT ou chaves de API antes de processar qualquer arquivo.
6.2. Rate limiting
Implemente limitação de taxa para evitar abuso: número máximo de uploads por minuto/hora por usuário ou IP.
# Configuração de rate limiting (Flask-Limiter)
limiter = Limiter(key_func=get_remote_address)
limiter.limit("10 per minute")(upload_route)
6.3. Políticas de expiração
Arquivos temporários ou não confirmados devem ser removidos automaticamente após período definido (ex.: 24 horas).
7. Monitoramento e Resposta a Incidentes
7.1. Logging detalhado
Registre todos os eventos de upload: timestamp, ID do usuário, nome original, tipo detectado, tamanho, hash do conteúdo.
import logging
def log_upload(usuario, nome_original, tipo_real, tamanho, hash):
logging.info(
f"Upload: user={usuario}, file={nome_original}, "
f"type={tipo_real}, size={tamanho}, hash={hash}"
)
7.2. Alertas para padrões anômalos
Configure alertas para atividades suspeitas: múltiplos uploads do mesmo tipo incomum, uploads em horários atípicos, tentativas de upload de arquivos bloqueados.
7.3. Procedimentos de quarentena
Arquivos suspeitos (detectados por antivírus ou heurísticas) devem ser movidos para uma área de quarentena isolada e notificados à equipe de segurança.
Referências
- OWASP File Upload Cheat Sheet — Guia completo de boas práticas para upload seguro de arquivos, mantido pela OWASP.
- Mozilla Web Security Guidelines: File Upload — Diretrizes de segurança da Mozilla para implementação de upload de arquivos.
- PortSwigger Research: File Upload Vulnerabilities — Artigo técnico detalhado sobre vulnerabilidades comuns em upload de arquivos e como explorá-las.
- Python Magic Documentation — Documentação oficial da biblioteca python-magic para detecção de tipos MIME via magic bytes.
- ClamAV Documentation — Documentação oficial do ClamAV, antivírus open source para detecção de malware em arquivos.
- Pillow (PIL Fork) Documentation — Documentação oficial da biblioteca Pillow para processamento e sanitização de imagens em Python.