1. Separa autorización, transferencia y publicación

Un upload directo evita que el servidor de aplicación retransmita cada byte. El navegador pide permiso, el backend autoriza una operación concreta y el cliente transfiere al storage. Después, la aplicación comprueba el objeto y decide si puede usarse. Ese último paso es parte del flujo, no una mejora opcional.

Define estados explícitos como pending, uploaded, scanning, ready y rejected. Una fila ready debe representar un objeto validado, no simplemente una respuesta exitosa del navegador. La interfaz puede mostrar progreso sin prometer disponibilidad antes de terminar la verificación.

2. Firma una clave creada por el servidor

Valida la sesión y el permiso para adjuntar archivos al recurso destino. Construye la clave con un tenant autorizado y un identificador aleatorio generado en el servidor. El nombre original sirve como metadato de presentación; no debe decidir el bucket ni permitir sobrescribir un objeto arbitrario.

Pseudocódigo
session = requireVerifiedSession(request)
resource = authorizeAttachment(session, resourceId)
uploadId = randomId()
key = "quarantine/" + resource.tenantId + "/" + uploadId
recordPendingUpload(uploadId, resource, key, expectedSize)
url = signPut(privateBucket, key, shortExpiry, signedHeaders)
return { uploadId, url, requiredHeaders: signedHeaders }
Esquema independiente del SDK. No acepta una clave de storage ni un tenant como autoridad desde el cliente.

Una URL firmada funciona como una capacidad temporal: quien la posea puede ejercer la operación permitida. No la registres completa en analytics o logs. Usa una duración acorde con el tamaño del archivo y contempla renovar el permiso tras volver a autorizar al usuario. Las credenciales que firman pueden vencer antes que el plazo solicitado.

Documentación: AWS: uso y vigencia de URLs prefirmadas

3. Define límites que realmente se apliquen

Un Content-Type enviado por el cliente no demuestra el formato del archivo. Combina una lista permitida de tipos y extensiones con inspección del contenido, tamaño máximo y validación específica del formato. Para documentos comprimidos, considera también tamaño expandido y consumo de CPU al procesarlos.

La forma de restringir una subida depende de PUT firmado, POST con política y las capacidades del proveedor compatible. No asumas que todas las implementaciones S3 aplican las mismas condiciones. Verifica qué encabezados forman parte de la firma y qué límites valida el storage; completa lo que falte en una verificación posterior sobre un objeto privado.

  • Define cuota por usuario o tenant y límite de operaciones pendientes.
  • Restringe CORS a los orígenes y métodos de tu aplicación; CORS no reemplaza autorización.
  • Mantén privado el bucket y evita publicar archivos desde el mismo origen de la aplicación sin controles apropiados.

Documentación: OWASP: validación y almacenamiento de uploads

4. Comprueba lo que llegó al storage

El endpoint de finalización recibe uploadId y resuelve la clave desde el registro pendiente. Vuelve a comprobar sesión, pertenencia y estado. Consulta metadatos del objeto para contrastar tamaño y las restricciones verificables; no aceptes como prueba un ETag enviado por el cliente ni lo interpretes universalmente como hash del archivo.

Inspecciona y analiza el contenido en cuarentena antes de hacerlo accesible. Una URL PUT puede reutilizarse mientras siga vigente; validar y luego servir la misma clave mutable deja una ventana de sustitución. Vincula el procesamiento a una versión concreta cuando el proveedor lo permita o crea una copia privada de procesamiento que el cliente no pueda sobrescribir. Valida esa copia y publica exactamente ese objeto.

EstadoQué puede hacer la aplicación
pending / uploadedMostrar progreso y permitir verificación; no servir contenido.
scanningProcesar una versión o copia inmutable para el cliente.
readyAutorizar lecturas del objeto exacto ya validado.
rejected / expiredInformar la causa permitida y programar limpieza.

Haz idempotente la finalización. Una segunda llamada para el mismo objeto ya validado debe devolver el estado existente. Si la identidad del objeto cambió o el permiso fue revocado, detén el flujo.

5. Autoriza también la descarga

Al leer un adjunto privado, resuelve su relación con el recurso y verifica acceso actual. Una ruta difícil de adivinar no es un permiso. Puedes entregar una URL de lectura temporal o servir el archivo a través de un componente que aplique autorización; elige según tamaño, revocación requerida y observabilidad.

Define Content-Disposition y un nombre de descarga saneado. Para formatos activos, considera un origen separado y evita renderizar contenido arbitrario dentro de tu aplicación. Revisa cómo afectan la caché y los enlaces compartidos al acceso cuando un usuario abandona un workspace. Un enlace ya emitido puede seguir siendo útil hasta que expire.

6. Presupuesta limpieza y recuperación

Los uploads abandonados consumen espacio aunque nadie llegue a pulsar guardar. Programa limpieza de registros vencidos y objetos huérfanos con un margen que respete transferencias en curso. Si usas multipart, contempla abortar cargas incompletas. Conserva trazabilidad suficiente para distinguir un abandono de un fallo del proveedor.

Estima almacenamiento medio, operaciones, transferencia, procesamiento y retención de versiones. El comparador normaliza tarifas de referencia, pero algunos planes tienen mínimos mensuales y otros cobran clases de operaciones de forma distinta. Calcula un escenario con tu tamaño medio de archivo, descargas por archivo y política de eliminación.

  • Prueba sesión vencida, permiso revocado y un upload de otro tenant.
  • Prueba tamaño incorrecto, formato disfrazado y repetición de finalización.
  • Prueba sobrescritura después de iniciar validación y caída del scanner.
  • Verifica que los objetos rechazados y pendientes nunca sean públicos.

Documentación: Cloudflare R2: clases de operaciones y precios · DigitalOcean Spaces: mínimos y consumo

Fuentes y alcance

Documentación consultada el 8 de septiembre de 2026. Los ejemplos y criterios de decisión son propuestas editoriales; adáptalos al contrato de tu aplicación y valídalos en tu entorno de pruebas autorizado.

Del diseño a la decisión

Compara almacenamiento de objetos

Revisa tarifas, límites, condiciones y fuentes de cada opción.

Abrir comparador