O upload seguro de arquivos em PHP não consiste em aceitar um formulário e mover um arquivo para o servidor. Um anexo pode se tornar um vetor de execução, um vazamento de informações, uma carga que esgota recursos ou um documento inacessível quando o processo de negócio precisa dele. O design deve abranger o ciclo completo: recebimento, validação, armazenamento, processamento, autorização de acesso, auditoria e exclusão.
Definir o contrato de negócio antes de aceitar arquivos

O primeiro controle não é técnico: é limitar qual necessidade cada anexo resolve. Um comprovante de identidade, uma fatura e uma imagem de perfil têm formatos, proprietários, períodos de retenção e permissões de consulta distintos. Agrupá-los em uma opção genérica de «enviar arquivo» dificulta a aplicação de controles adequados.
Para cada tipo documental, defina um contrato explícito:
- Quais formatos são necessários e quais ficam excluídos.
- Tamanho máximo, número máximo de anexos por operação e cota acumulada por conta ou processo.
- Quem pode enviá-lo, em qual estado do processo e se pode substituí-lo ou excluí-lo.
- Qual validação automática e qual revisão humana ele precisa.
- Quem pode visualizá-lo, baixá-lo ou solicitar uma nova versão.
- Por quanto tempo é mantido e qual evento aciona sua exclusão.
Esse contrato evita aceitar arquivos «por precaução». Também permite distinguir uma falha de validação, que o usuário pode corrigir, de uma restrição de negócio, como tentar anexar um documento quando o processo já está encerrado.
Por que extensão e tipo declarado não são suficientes
A extensão do nome original e o tipo enviado em $_FILES['type'] são dados fornecidos pelo cliente. Eles servem como informação auxiliar de interface, mas não comprovam o conteúdo. Renomear um arquivo é trivial, e um cliente pode enviar qualquer cabeçalho HTTP.
A validação técnica deve usar uma defesa em camadas. Em PHP, detecte o tipo a partir dos bytes recebidos com mecanismos como finfo; depois, aplique validadores específicos do formato quando o risco ou o uso exigirem. Para uma imagem que será exibida, não basta identificá-la como imagem: é recomendável decodificá-la com uma biblioteca adequada e gerar uma nova representação. Para um PDF ou um documento de escritório, valide a estrutura esperada com ferramentas especializadas em um ambiente isolado.
Uma lista de permitidos é mais segura do que uma lista de bloqueados. Se o caso de uso aceita JPEG e PNG, rejeite o restante em vez de tentar enumerar formatos perigosos. Arquivos compactados exigem um controle adicional: um limite de tamanho compactado não limita a expansão ao descompactar. Estabeleça limites de tamanho expandido, número de entradas, profundidade de aninhamento e tempo de análise.
Os nomes também não são confiáveis. Não os use como caminho, identificador ou nome físico. Sequências como ../, caracteres de controle, extensões duplas e colisões de nomes devem perder relevância porque o sistema gera seu próprio identificador opaco.
Recebimento controlado e gestão de erros parciais
Antes de processar o conteúdo, limite a superfície de entrada. Configure limites coerentes em PHP, no servidor web e no proxy reverso. Se o proxy aceita 100 MB, mas o PHP admite 10 MB, o comportamento será confuso; se o PHP permitir mais do que o previsto, um invasor poderá consumir memória, disco temporário ou conexões de trabalho.
Controle explicitamente o tamanho individual, o total da requisição, o número de arquivos e a duração do upload. Verifique o código de erro de cada entrada de $_FILES, confirme que ela provém de um upload HTTP e trate cada anexo como uma unidade independente. Em uma operação múltipla, decida antecipadamente se o resultado é atômico ou parcial. Se forem aceitos resultados parciais, a resposta deve indicar com precisão qual arquivo foi recebido, qual foi rejeitado e por quê, sem revelar detalhes internos do servidor.
Não processe um arquivo diretamente de um caminho controlado pela requisição nem confie que um upload interrompido seja inofensivo. Os temporários incompletos devem ser limpos, e as tentativas devem ser idempotentes quando o produto as suportar. Um token de operação ou uma chave de idempotência evita criar vários anexos equivalentes após reenvios de rede.
Armazenamento privado, quarentena e processamento
Os binários não devem ficar sob o diretório público da aplicação. Armazene-os em armazenamento privado, com credenciais de privilégio mínimo, e associe cada objeto a um identificador interno gerado pelo sistema. O banco de dados pode manter metadados como proprietário, contexto de negócio, tipo detectado, tamanho, hash criptográfico, estado, datas e política de retenção. Evite armazenar dados pessoais desnecessários no nome ou em registros operacionais.
Um fluxo robusto separa recebimento e disponibilidade:
- A aplicação recebe o arquivo e cria um registro no estado
pendenteouem_quarentena. - O binário é depositado em uma localização não acessível para downloads.
- Um processo assíncrono realiza análise antimalware, validação profunda, transformação ou extração permitida.
- O resultado muda para
disponivel,rejeitadoourequer_revisao. - A interface mostra o estado operacional sem fingir que um upload já pode ser utilizado.
As filas reduzem o tempo de resposta, mas introduzem falhas próprias: trabalhos duplicados, mensagens atrasadas e processadores inoperantes. Projete tarefas idempotentes, limites de nova tentativa, alertas para itens parados e um caminho de reprocessamento controlado. Se a análise não estiver disponível, a alternativa segura geralmente é manter o anexo em quarentena, e não publicá-lo.
Autorizar cada download e entregar conteúdo sem executá-lo
O fato de alguém conhecer um identificador não lhe concede acesso. Cada download deve verificar a identidade autenticada, sua relação atual com o recurso e o contexto de negócio: pertencimento a uma organização, atribuição ao processo, função vigente, estado do documento e restrições temporais. Não reutilize a autorização que existia no upload do arquivo; as permissões podem ter sido revogadas depois.
O download deve passar por um controlador que aplica essa decisão antes de ler ou delegar o objeto. URLs assinadas podem ser úteis para entregar a partir de armazenamento externo, mas exigem escopo limitado, expiração curta e controles que impeçam sua emissão para recursos não autorizados.
Entregue tipos ativos com cautela. Para documentos que não se destinam a ser renderizados no navegador, use Content-Disposition: attachment. Defina o tipo de conteúdo a partir do tipo validado, não da extensão, e adicione X-Content-Type-Options: nosniff. Uma pré-visualização não é um simples download incorporado: ela deve usar representações transformadas, isolar conteúdo potencialmente ativo e evitar revelar caminhos internos.
Auditoria, recuperação e testes antes da produção

Registre eventos relevantes: criação, substituição, alteração de estado, download, exclusão, erro de análise e modificação de permissões. O registro deve conter ator, momento, recurso e resultado, mas não precisa armazenar o binário nem dados confidenciais duplicados. Proteja esses eventos contra alterações e defina quem pode consultá-los.
A recuperação exige saber o que ocorre se o armazenamento, o banco de dados ou o processador falhar. Não marque um documento como disponível até que o binário e seus metadados estejam consistentes. Projete tarefas de reconciliação para detectar registros sem objeto, objetos órfãos e itens que permanecem tempo demais em quarentena.
Lista de verificação de saída
- Os formatos permitidos correspondem a um caso de uso documentado e são validados pelo conteúdo.
- Existem limites de tamanho, quantidade, tempo e expansão de arquivos compactados.
- Os nomes originais não determinam caminhos nem nomes físicos.
- Os binários são armazenados fora do diretório público e passam por quarentena quando aplicável.
- O download reavalia a autorização e não expõe caminhos internos.
- São testados arquivos malformados, duplicados, uploads interrompidos, permissões revogadas e falhas de armazenamento.
- Há monitoramento de rejeições, trabalhos parados, erros de entrega e capacidade consumida.
- As políticas de retenção, exclusão e auditoria têm responsável operacional.
Transformar um upload em um fluxo com estados e controles pode parecer mais caro do que usar um diretório de uploads. No entanto, reduz decisões improvisadas quando surge um arquivo suspeito, uma reivindicação de acesso ou uma interrupção operacional. Essa rastreabilidade faz parte do produto, não é um acréscimo posterior.



