Central de Ajuda

Guia da Central de Ajuda

Upload de arquivos com PHP: formulário, validação, limites e armazenamento seguro

Um upload recebe conteúdo controlado pelo visitante e, se for mal implementado, pode permitir execução de código, sobrescrita de arquivos, malware, XSS ou esgotamento de disco. Validar apenas a extensão ou o tipo enviado pelo navegador não é suficiente. Defina quem pode enviar, quais formatos são aceitos, tamanho, destino, retenção e forma de servir o arquivo.

Formulário HTML

<form action="upload.php" method="post" enctype="multipart/form-data">
  <input type="hidden" name="csrf" value="TOKEN_DA_SESSAO">
  <label>Documento
    <input type="file" name="documento" accept="application/pdf" required>
  </label>
  <button type="submit">Enviar</button>
</form>

O atributo enctype="multipart/form-data" é obrigatório. accept ajuda a interface, mas pode ser contornado e não substitui validação no servidor.

Exemplo de processamento seguro

<?php
declare(strict_types=1);
session_start();

if (!hash_equals($_SESSION['csrf'] ?? '', $_POST['csrf'] ?? '')) {
    http_response_code(403);
    exit('Solicitação inválida.');
}

$arquivo = $_FILES['documento'] ?? null;
if (!is_array($arquivo) || is_array($arquivo['error'] ?? null)) {
    throw new RuntimeException('Upload inválido.');
}
if ($arquivo['error'] !== UPLOAD_ERR_OK) {
    throw new RuntimeException('Falha no upload: ' . $arquivo['error']);
}
if ($arquivo['size'] > 5 * 1024 * 1024) {
    throw new RuntimeException('Arquivo acima de 5 MB.');
}

$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($arquivo['tmp_name']);
$extensoes = ['application/pdf' => 'pdf'];
if (!isset($extensoes[$mime])) {
    throw new RuntimeException('Formato não permitido.');
}

$nome = bin2hex(random_bytes(16)) . '.' . $extensoes[$mime];
$destino = dirname(__DIR__) . '/storage/uploads/' . $nome;
if (!move_uploaded_file($arquivo['tmp_name'], $destino)) {
    throw new RuntimeException('Não foi possível armazenar o arquivo.');
}

O que o exemplo ainda precisa em produção?

  • autenticação e autorização por usuário/objeto;
  • token CSRF criado e rotacionado corretamente;
  • tratamento amigável dos códigos UPLOAD_ERR_*;
  • limite de quantidade e frequência;
  • scanner de malware quando o risco justificar;
  • registro de auditoria sem conteúdo sensível;
  • política de retenção e exclusão;
  • download por controlador autorizado ou armazenamento de objetos privado.

Não confie no nome e MIME do navegador

$_FILES['type'] e o nome original são fornecidos pelo cliente. Detecte o tipo pelo conteúdo com Fileinfo, mantenha uma allowlist pequena e gere um nome aleatório. Alguns formatos podem conter conteúdo ativo mesmo quando são válidos; imagens podem ser reprocessadas e documentos podem exigir sandbox/antivírus.

Armazene fora do diretório público

O ideal é guardar uploads fora do document root e entregá-los por código que valida autorização. Se precisarem ser públicos, use domínio estático sem execução de PHP, cabeçalhos seguros, nomes gerados e permissões mínimas. Nunca permita que um arquivo enviado se torne .php, .phtml, configuração do servidor ou substitua conteúdo existente.

Limites que participam do upload

CamadaExemplos
PHPfile_uploads, upload_max_filesize, post_max_size, max_file_uploads, max_input_time
Servidor web/proxylimite de corpo e timeout
Aplicaçãotamanho, quantidade, usuário e tipo
Contaquota, inode, diretório temporário e permissões

post_max_size precisa comportar a requisição completa e normalmente deve ser maior que upload_max_filesize. Aumentar limites globais pode facilitar abuso; altere somente o domínio/aplicação necessária.

Diagnóstico

  1. Registre o código de erro de $_FILES.
  2. Compare tamanho com limites PHP, proxy e aplicação.
  3. Confira espaço e escrita no diretório temporário e destino.
  4. Verifique a configuração da SAPI usada pelo site, não apenas CLI.
  5. Consulte log do PHP/servidor no horário exato.
  6. Teste arquivo pequeno permitido antes de elevar recursos.

Referências: uploads no manual PHP e move_uploaded_file(). Veja também configuração do php.ini.

Recursos relacionados

Continue com materiais e serviços relacionados ao tema deste guia.

Este guia resolveu sua dúvida? Seu retorno ajuda a priorizar as próximas revisões.
Atendimento técnico

Não encontrou o resultado esperado?

Abra um chamado e envie a mensagem de erro completa, o domínio ou serviço afetado, o horário do teste e capturas de tela sem senhas.