Lista de verificação de implementação de validação de arquivo em formulário web
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.
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 teste | Resultado esperado | arquivo a ser usado |
|---|---|---|
| Arquivo no limite exato | Sucesso | <a href="/ja/files/threshold/">Arquivos de limite</a> |
| Arquivo que excede o limite por 1 byte | Erro 413 | <a href="/ja/files/threshold/">Arquivos de limite</a> |
| Arquivo vazio de 0 bytes | Erro de validação | Criação manual |
| Arquivo com extensão falsificada (PHP → .jpg) | Erro MIME | <a href="/ja/files/broken/">Arquivo quebrado</a> |
| Arquivo com cabeçalho corrompido | Erro 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.
- <strong>validação de tipo MIME no servidor</strong>(usando <code>finfo</code>)— não confie na declaração do cliente
- <strong>Salvar com nome de arquivo aleatório</strong> — não use o nome de arquivo original
- <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>