Skip to content

Lista de verificação de implementação de validação de arquivo em formulário web

Categoria:Segurança · Implementação
Este artigo está disponível atualmente apenas em japonês. As versões traduzidas serão publicadas sequencialmente.

A implementação da funcionalidade de upload de arquivo possui muitos pontos de atenção em termos de segurança e várias armadilhas que são fáceis de negligenciar. Este artigo explica um checklist para implementar uploads de arquivo que funcionam com segurança em ambiente de produção.

Ordem recomendada para validação de arquivo Ordem segura: de validações leves para validações pesadas 1. File size bytes <= max NG → reject imediato 2. Extension .jpg / .png ... NG → reject imediato 3. MIME type finfo / mime_content_type NG → reject imediato 4. Magic bytes FF D8 FF (JPEG) ... NG → reject imediato 5. Content scan ClamAV / VirusTotal NG → reject imediato
Figura 1: Validação executada com verificações leves primeiro e rejeição antecipada

Visão geral da lista de verificação

Esta lista de verificação é organizada em torno da <strong>validação de back-end (lado do servidor)</strong>. A validação de front-end é implementada como uma melhoria de UX auxiliar, mas não fornece garantias de segurança.

1. Validação do tamanho do arquivo

  • <input type="checkbox" disabled> O limite de upload é definido em bytes (sem confundir MB e MiB)
  • <input type="checkbox" disabled> No caso de PHP, ambos <code>upload_max_filesize</code> e <code>post_max_size</code> são configurados
  • <input type="checkbox" disabled> No caso de Nginx, o overhead de multipart é adicionado a <code>client_max_body_size</code>
  • <input type="checkbox" disabled> Há tratamento de erro quando <code>$_FILES['file']['error']</code> é UPLOAD_ERR_INI_SIZE / UPLOAD_ERR_FORM_SIZE
  • <input type="checkbox" disabled> Verifica tamanho mínimo de arquivo(exclui arquivos de 0 bytes)
 $maxBytes) {
        throw new \RuntimeException(sprintf(
            'ファイルサイズ(%s)が上限(%s)を超えています',
            number_format($file['size']),
            number_format($maxBytes)
        ));
    }
}

2. Validação de formato de arquivo (tipo MIME)

  • <input type="checkbox" disabled> O <code>Content-Type</code> enviado do cliente (<code>$_FILES['file']['type']</code>) não é confiável
  • <input type="checkbox" disabled> Há validação de tipo MIME no lado do servidor usando <code>finfo</code> / <code>mime_content_type()</code>
  • <input type="checkbox" disabled> Define whitelist de tipos MIME permitidos
file($file['tmp_name']);

if (!in_array($mimeType, $allowed, true)) {
    throw new \RuntimeException('許可されていないファイル形式です: ' . $mimeType);
}

3. Validação de extensão de arquivo

  • <input type="checkbox" disabled> Define whitelist de extensões(não blacklist)
  • <input type="checkbox" disabled> Detecta e rejeita extensões duplas(<code>shell.php.jpg</code>)
  • <input type="checkbox" disabled> Normaliza e valida maiúsculas/minúsculas(<code>.JPG</code> e <code>.jpg</code> são tratados como iguais)
 2) {
    throw new \RuntimeException('不正なファイル名です');
}

$ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
if (!in_array($ext, $allowed, true)) {
    throw new \RuntimeException('許可されていない拡張子です: ' . $ext);
}

4. Validação de magic byte (assinatura de arquivo)

  • <input type="checkbox" disabled> Validando magic bytes em arquivos importantes (excluindo arquivos executáveis, etc.)

5. Tratamento seguro do destino e nome do arquivo

  • <input type="checkbox" disabled> Diretório de destino está fora da raiz web(ou controlado via XSendFile/X-Accel-Redirect)
  • <input type="checkbox" disabled> Nome do arquivo salvo é gerado aleatoriamente(como UUID), não usa o nome original
  • <input type="checkbox" disabled> Remove path traversal(<code>../../../etc/passwd</code>)através de validação
  • <input type="checkbox" disabled> Diretório de destino não tem permissão de execução PHP(desabilitar PHP via <code>.htaccess</code> ou configuração Nginx)

6. Tratamento de erros e resposta

  • <input type="checkbox" disabled> Um status HTTP apropriado (200/201) é retornado quando o upload é bem-sucedido
  • <input type="checkbox" disabled> <strong>413 Payload Too Large</strong> é retornado quando o tamanho é excedido
  • <input type="checkbox" disabled> Retorna <strong>422 Unprocessable Entity</strong> para formato de arquivo inválido
  • <input type="checkbox" disabled> As mensagens de erro não contêm informações internas do servidor (caminho, versão, etc.)

7. Casos de teste

Após a implementação, execute os seguintes casos de teste para confirmar o funcionamento. Você pode usar arquivos de teste do DevLab.

Casos de testeResultado esperadoarquivo a ser usado
Arquivo no limite exatoSucesso<a href="/ja/files/threshold/">Arquivos de limite</a>
Arquivo que excede o limite por 1 byteErro 413<a href="/ja/files/threshold/">Arquivos de limite</a>
Arquivo vazio de 0 bytesErro de validaçãoCriação manual
Arquivo com extensão falsificada (PHP → .jpg)Erro MIME<a href="/ja/files/broken/">Arquivo quebrado</a>
Arquivo com cabeçalho corrompidoErro de validação<a href="/ja/files/broken/">Arquivo quebrado</a>

Resumo

A implementação de upload seguro de arquivos requer validação em múltiplas camadas. Você deve implementar obrigatoriamente os 3 pontos a seguir.

  1. <strong>validação de tipo MIME no servidor</strong>(usando <code>finfo</code>)— não confie na declaração do cliente
  2. <strong>Salvar com nome de arquivo aleatório</strong> — não use o nome de arquivo original
  3. <strong>Desabilitar execução de PHP no diretório de destino</strong> — Impedir que scripts sejam executados no diretório de upload

Arquivo de teste disponível para usar neste artigo

  • → <a href="/ja/files/broken/" class="text-primary-600 dark:text-primary-400 hover:underline">Lista de arquivos corrompidos (para teste de erros de validação)</a>
  • → <a href="/ja/files/threshold/" class="text-primary-600 dark:text-primary-400 hover:underline">Lista de arquivos de teste de limite (9.9MB / 10MB / 10.1MB)</a>
  • → <a href="/ja/files/images/" class="text-primary-600 dark:text-primary-400 hover:underline">Lista de arquivos de teste de imagem (PNG / JPG / WebP / GIF)</a>

Artigos relacionados

  • → <a href="/ja/blog/how-to-test-upload-limit/" class="text-primary-600 dark:text-primary-400 hover:underline">Como testar corretamente o limite de upload de arquivo</a>
  • → <a href="/ja/reference/magic-bytes/" class="text-primary-600 dark:text-primary-400 hover:underline">Referência de Magic Bytes (Assinatura de Arquivo)</a>