Como descobrir o tipo real de um arquivo pelos magic bytes
O desafio
Um usuário enviou isto como 'avatar.png'. Um PNG de verdade tem que começar com a assinatura 89 50 4E 47. Leia os primeiros bytes do arquivo e identifique qual é o tipo real dele.
O que você vai aprender
- Entender o que são magic bytes (uma assinatura de arquivo) e onde eles ficam dentro do arquivo
- Ler um hex dump junto com a coluna ASCII para reconhecer o formato real de um arquivo
- Explicar por que o nome do arquivo e o Content-Type alegado não provam nada sobre seu conteúdo
- Reconhecer como atacantes disfarçam um tipo de arquivo como outro para burlar checagens de extensão
- Descrever como a validação de assinatura no servidor fortalece um pipeline de upload de arquivos
Habilidades testadas
Pré-requisitos
- Familiaridade com leitura de hexadecimal e ASCII básico
- Consciência de que arquivos têm uma estrutura binária definida, não apenas uma extensão
Como funciona
Quase todo formato de arquivo começa com uma sequência curta e fixa de bytes chamada de número mágico ou assinatura de arquivo. Os programas leem esses bytes iniciais para decidir o que o arquivo realmente é antes de fazer qualquer coisa com ele. Uma imagem PNG, por exemplo, sempre começa com 89 50 4E 47 (os bytes soletram .PNG na coluna ASCII), um JPEG começa com FF D8 FF, um PDF começa com 25 50 44 46 (%PDF), e um arquivo ZIP começa com 50 4B 03 04 (PK..). A assinatura fica bem no início do arquivo e não muda só porque alguém renomeia o arquivo ou define um cabeçalho Content-Type diferente.
Esse é o ponto central deste desafio: o upload se chama avatar.png, mas um nome é apenas um rótulo que o usuário escolhe. A verdade está nos bytes. Abrir o arquivo em uma visualização hexadecimal mostra duas colunas - o hexadecimal bruto à esquerda e uma coluna ASCII imprimível à direita. Quando os bytes iniciais não correspondem à assinatura prometida pela extensão, o arquivo não é o que afirma ser. A coluna ASCII é seu atalho: muitas assinaturas são texto legível por humanos, então uma olhada rápida costuma revelar o formato real mais rápido do que decorar hexadecimal.
Isso importa porque o tipo de arquivo é uma fronteira de segurança. Um pipeline de imagens espera decodificar pixels; se em vez disso receber um executável, um script ou um arquivo compactado, as premissas quebram, e um atacante que consiga passar o tipo errado por uma checagem fraca ganha um ponto de apoio. Ler magic bytes é a habilidade forense que fecha essa brecha.
Erros comuns
- Confiar na extensão do arquivo. Um final
.pngé apenas texto no nome do arquivo; qualquer um pode renomear qualquer coisa. A extensão é uma pista, nunca uma prova. - Confiar no cabeçalho Content-Type. Quem define esse valor é o navegador ou o cliente, e o cliente está totalmente sob controle do atacante. Um
image/pngna requisição não diz nada sobre os bytes reais. - Ler apenas o hexadecimal e ignorar a coluna ASCII. Muitas assinaturas soletram letras reconhecíveis na coluna da direita - ignorá-la só faz você trabalhar mais do que precisa.
- Assumir que um início com aparência válida significa que o arquivo inteiro é seguro. Magic bytes correspondentes confirmam o formato, mas não garantem que o restante do arquivo seja benigno ou esteja bem formado.
Como se proteger
Ao aceitar uploads de arquivos, nunca decida o tipo de um arquivo pelo nome ou pelo cabeçalho Content-Type. Leia os bytes iniciais reais no servidor e verifique-os contra a assinatura do formato esperado, depois aplique uma lista de permissão dos tipos aceitos em vez de uma lista de bloqueio dos tipos proibidos.
Adicione camadas extras de defesa para que nem mesmo um arquivo do tipo correto possa causar dano:
- Reprocesse ou recodifique as imagens aceitas usando uma biblioteca confiável, que rejeita entradas malformadas ou disfarçadas.
- Armazene os uploads fora da raiz web e sirva-os a partir de um domínio separado com um
Content-Dispositionforçado, para que nunca sejam executados ou interpretados na sua origem. - Gere seus próprios nomes de arquivo aleatórios e ignore completamente o nome fornecido pelo usuário.
- Limite o tamanho do arquivo e valide as dimensões antes de qualquer processamento pesado.