Descargas de archivos seguras con URLs firmadas: una configuración práctica
Descargas de archivos seguras con URLs firmadas: evita path traversal, aplica tipos de contenido seguros y expira enlaces para que los archivos privados sigan siendo privados.

Por qué las descargas privadas se convierten accidentalmente en públicas
La mayoría de las “descargas privadas” empiezan con una idea simple: poner un archivo en un servidor, añadir una ruta como /download?file=... y confiar en que la app solo muestre el botón a usuarios autenticados. El problema es que los archivos no se descargan desde el botón. Se descargan desde una dirección que puede ser copiada, compartida, adivinada o solicitada por un script.
Un fallo común es servir un archivo privado de la misma forma que sirves una página pública. Si la URL de descarga funciona sin comprobaciones estrictas cada vez que se solicita, no importa dónde se mostró. Cualquiera puede llamar al endpoint directamente y la app puede devolver el archivo.
Las “URLs ocultas” no son protección. Nombres de archivo aleatorios y rutas largas solo ralentizan una adivinación casual. No impiden:
- Que un usuario comparta el enlace con otra persona
- Que el historial del navegador, logs, capturas de pantalla o tickets de soporte filtren la URL
- Bots que escanean patrones previsibles (como
/uploads/o/invoices/) - Un bug que exponga un listado de directorio o permita trucos con
../
Los endpoints de descarga son objetivos atractivos porque a menudo tocan datos sensibles (facturas, contratos, exportaciones) y porque son fáciles de probar: envía peticiones, mira lo que responde, repite. Si el endpoint acepta un nombre de archivo desde la petición, un atacante puede sondear por archivos de otros usuarios o probar traversal para escapar de la carpeta prevista.
Las URLs firmadas cambian las reglas haciendo que la propia URL lleve la prueba de que fue emitida por tu servidor para un propósito específico. Una URL firmada adecuada suele vincular qué archivo está permitido, qué permisos aplican (a veces vía usuario u organización) y cuánto tiempo debe funcionar el enlace.
Las URLs firmadas no son mágicas. No arreglan un modelo de autorización roto y no ayudan si tu servidor firma “cualquier ruta que el usuario pida”. Tampoco evitan que alguien comparta un enlace mientras siga siendo válido.
Un ejemplo simple: un agente de soporte copia la URL de descarga de una factura desde una herramienta de administración y la pega en un chat. Si esa URL solo parece “secreta”, puede funcionar para siempre y convertirse en un endpoint público permanente. Si es un enlace firmado que expira y está ligado a esa factura, es más difícil de usar mal y deja de funcionar tras una ventana corta.
URLs firmadas, explicadas sin jerga
Una URL firmada es un enlace de descarga normal con una “prueba” extra adjunta. La prueba es una firma: una cadena corta que tu servidor crea usando una clave secreta que solo el servidor conoce.
Cuando alguien hace clic en el enlace, el servidor puede saber si la URL fue emitida por tu app o si fue manipulada. Esa es la idea central.
¿Qué se firma realmente?
La firma se calcula sobre unos pocos valores específicos, convertidos en un único mensaje y luego “sellados” con tu clave secreta. Una descarga firmada típica incluye:
- El identificador del archivo (no una ruta de sistema de archivos)
- Un tiempo de expiración (una marca de tiempo)
- Contexto opcional como ID de usuario, ID de organización o la acción prevista (download)
- A veces el método HTTP (GET) para que la firma no pueda reutilizarse en otras solicitudes
Si alguna de esas partes cambia, la firma ya no coincide.
La expiración importa porque los enlaces se filtran. La gente los reenvía, el historial del navegador los guarda, los logs los capturan y hay capturas de pantalla. Los enlaces de descarga que expiran limitan el radio de impacto cuando esa fuga es inevitable.
Qué comprueba el servidor antes de enviar el archivo
Cuando llega una petición, tu servidor repite el mismo cálculo de firma usando los valores en la petición. Si la firma calculada coincide con la que está en la URL y la marca de tiempo sigue siendo válida, la petición pasa la primera puerta.
Después, el servidor todavía aplica permisos reales. Si Alice hace clic en un enlace a la factura de Bob, la firma podría ser válida, pero Alice aun así debe recibir una respuesta de “no permitido” porque el archivo no le pertenece.
Las URLs firmadas aparecen en unas pocas formas comunes:
- Parámetros de consulta (fácil de usar, lo más común)
- Cabeceras Authorization (URLs limpias, más difícil de compartir)
- Cookies (útiles para descargas en navegador sin exponer tokens en la URL)
Un error común en código prototipo es implementar la firma “a medias” (solo comprobación de firma, sin verificación de propiedad). Ahí es donde los archivos privados se vuelven públicos una vez que se descubre el formato de la URL.
Previene el path traversal no confiando nunca en rutas de archivo
El path traversal ocurre cuando una app deja que un usuario influya en la ruta del archivo y el usuario la usa para alcanzar archivos que no debería ver. El ejemplo clásico es ../ (subir una carpeta), pero los atacantes rara vez se quedan ahí. Prueban versiones codificadas como %2e%2e%2f, cadenas doblemente codificadas, barras invertidas (..\\\\) que funcionan en Windows o separadores extraños que luego el SO normaliza.
Por eso es arriesgado aceptar en un endpoint algo como ?file=reports/2025/invoice.pdf. Incluso si añades una comprobación rápida para ../, puedes ser derrotado por el orden de decodificación (decodificar una vez vs dos veces), barras mezcladas o un framework que normaliza la ruta después de que la validaste.
El patrón más seguro es simple: los usuarios nunca pasan una ruta. Pasan un ID opaco (o un token firmado) y el servidor busca la ubicación real en el almacenamiento.
Ejemplo: en vez de GET /download?file=..., usa GET /download?docId=8f31.... En el servidor, recupera docId de la base de datos, confirma que quien solicita tiene permiso para acceder a él, y luego lee la ruta almacenada exacta (o la clave de objeto) que tú creaste. El usuario nunca tiene la oportunidad de “apuntar” la llamada de lectura de archivos.
Si debes trabajar con rutas (por ejemplo, almacenamiento en disco local), normaliza y valida antes de cualquier acceso a archivos. Una buena regla es: construye la ruta final desde un directorio base conocido más un nombre relativo seguro conocido, y luego confirma que sigue dentro del directorio base después de la normalización.
Comprobaciones prácticas que aguantan mejor que filtros frágiles de cadenas:
- Rechaza rutas absolutas (las que empiezan con
/,\\\\o un prefijo de unidad comoC:). - Decodifica una vez, normaliza y luego valida. No valides la cadena cruda.
- Bloquea separadores inesperados (mezcla de
/y\\\\) y bytes nulos. - Tras unir base + solicitado, confirma que la ruta normalizada comienza con la base normalizada.
- Prefiere una lista blanca de claves de archivo conocidas desde la base de datos en lugar de cualquier nombre de archivo proporcionado por el usuario.
Muchos manejadores de descarga rotos fallan aquí porque se centran en la parte de la URL firmada y olvidan que la ruta sigue siendo controlada por el atacante.
Impone tipos de contenido para que los archivos no se ejecuten como páginas web
Las URLs firmadas controlan quién puede obtener un archivo. No controlan cómo un navegador trata ese archivo una vez obtenido. Si sirves un archivo subido con cabeceras equivocadas, una “descarga” puede convertirse en una página que se ejecuta en la sesión de tu usuario.
Una sorpresa común es la detección heurística del tipo de contenido. Incluso si envías un tipo genérico, algunos navegadores intentan adivinar qué es el archivo. Si el contenido parece HTML o JavaScript, el navegador puede renderizarlo en lugar de descargarlo. Así es como una subida que parecía inofensiva se convierte en un script que se ejecuta.
Establece las cabeceras correctas siempre
Haz que tu endpoint de descarga sea el único lugar que fije cabeceras. No confíes en lo que la capa de almacenamiento “cree” que es el tipo.
Como mínimo, fija estas cabeceras en la respuesta:
Content-Type: permite solo tipos que esperas (por ejemploapplication/pdf,image/png).Content-Disposition: attachment; filename="...": fuerza la descarga en vez del render inline.X-Content-Type-Options: nosniff: le dice al navegador que no adivine.
También trata el nombre de archivo como entrada no confiable. Si construyes filename con datos de usuario, elimina separadores de ruta y caracteres de control. Manténlo simple: letras, números, puntos, guiones y guiones bajos. Si necesitas el nombre “bonito”, almacénalo separado de la clave de almacenamiento.
Bloquea tipos riesgosos y usa un valor por defecto seguro
Si los usuarios pueden subir archivos, decide qué no servirás directamente. HTML, SVG y cualquier cosa que contenga scripts son de alto riesgo porque pueden ejecutarse al abrirse.
Una política simple que funciona bien:
- Permite una lista pequeña de tipos de descarga conocidos (PDF, imágenes comunes, CSV si lo necesitas).
- Rechaza o cuarentena
text/html,image/svg+xmly cualquier tipo relacionado con JavaScript. - Si falta el tipo del archivo o no coincide con tu lista blanca, sírvelo como
application/octet-stream. - Usa siempre
Content-Disposition: attachmentpara archivos subidos por usuarios.
Ejemplo concreto: si alguien sube “invoice.html” pero tú lo sirves con Content-Type: text/html y render inline, abrir ese enlace puede ejecutar scripts. Si fuerzas application/octet-stream más attachment, el mismo archivo se convierte en una descarga que no se ejecutará en el navegador.
Cómo firmar y validar URLs de forma segura
Una URL firmada es tan segura como lo que firmas y lo estrictamente que lo validas. El objetivo es simple: el servidor debe poder probar que la petición estaba permitida para un archivo específico, por un tiempo limitado y (si hace falta) para un usuario específico, sin confiar en nada que envíe el navegador.
Un patrón de firma seguro
Mantén el secreto de firma solo en el servidor. No lo incluyas en el cliente, no lo pongas en variables de entorno expuestas al build del navegador y nunca lo incluyas en la URL. La URL debe llevar solo datos públicos más una firma.
Firma un conjunto pequeño y estricto de campos y trata todo lo demás como no confiable. Un conjunto mínimo práctico es:
rid: un ID de recurso (no una ruta de archivo)exp: una marca de tiempo de expiraciónsub: un ID de usuario o de tenant cuando las descargas están vinculadas a usuariossig: la firma (por ejemplo, un HMAC)
En el servidor, crea una cadena canónica en un orden fijo (por ejemplo rid=...\u0026exp=...\u0026sub=...). Luego firma esa cadena exacta. Al validar, reconstruye la cadena canónica desde los valores parseados, recalcula la firma y compara.
Usa una comparación en tiempo-constante para la verificación de la firma. La igualdad normal de cadenas puede filtrar pequeñas diferencias de tiempo que ayudan a un atacante a adivinar una firma con muchas peticiones.
Sé estricto al parsear. Si falta un campo, está duplicado, está malformado o fuera de rango, rechaza la petición. También rechaza cualquier parámetro extra que no hubieras firmado. De otro modo alguien puede añadir \u0026role=admin o \u0026download=true y engañar a código posterior que lea esos valores.
Aquí está la forma de la lógica de validación (agnóstica al lenguaje):
allowed = {rid, exp, sub, sig, kid}
if any query key not in allowed: reject
parse rid, exp, sub (strict types)
if now \u003e exp: reject
canonical = "rid=...\u0026exp=...\u0026sub=..."
expected = HMAC(secret_for(kid), canonical)
if !constant_time_equals(sig, expected): reject
serve file for rid (after authz check)
Finalmente, planifica la rotación de claves. Añade un pequeño kid (identificador de clave) para poder mantener una clave antigua disponible brevemente mientras los enlaces nuevos usan la clave nueva. Los enlaces antiguos deberían expirar pronto, así que no tienes que soportar claves viejas por mucho tiempo.
Enlaces que expiran y reglas de acceso que realmente aguantan
Una URL firmada que expira solo es útil si la expiración encaja con lo que el usuario intenta hacer. Si es demasiado corta generas tickets de soporte. Si es demasiado larga creas una puerta trasera silenciosa: un archivo “privado” que sigue siendo compartible durante días.
Una regla simple: establece la expiración en la ventana más corta que aún permita completar la tarea. Ver un documento ahora puede necesitar 5 a 15 minutos. Descargar una exportación grande puede necesitar 30 a 60 minutos. Si la acción puede reanudarse después, genera un enlace nuevo cuando el usuario inicie sesión de nuevo.
Enlaces de un solo uso vs reutilizables
Los enlaces de un solo uso reducen el riesgo de compartir, pero pueden romper flujos reales (cambio de app en móvil, Wi‑Fi inestable, gestores de descarga que reintentan). Los enlaces reutilizables son más amables, pero necesitan controles más estrictos.
Opciones prácticas que suelen aguantar:
- Enlace reutilizable de corta duración para descargas normales.
- Enlace de un solo uso para archivos muy sensibles (nóminas, claves privadas, documentos legales).
- Enlace reutilizable con un tope de descargas (por ejemplo, máximo 3 descargas exitosas).
- Enlace reutilizable acotado a un usuario u organización (la firma incluye userId/orgId y fileId).
- Enlace reutilizable ligado a una sesión cuando necesitas “debe estar autenticado”.
Para reducir el abuso, añade límites de velocidad en el endpoint que valida la firma. Incluso si la URL está firmada, puede ser bombardeada. Pon un techo de peticiones por IP y por usuario, y considera bloquear tras fallos repetidos (firmas malas, timestamps expirados).
El logging importa para auditorías, pero mantenlo mínimo. Guarda fileId, userId/orgId, timestamp y resultado (éxito/denegado). Evita registrar la URL completa o la query string porque puede contener la firma.
Ejemplo: una app de facturación puede emitir un enlace firmado de 15 minutos para un PDF de factura, acotado al orgId del cliente. Si un enlace llega a un proveedor, falla porque el orgId no coincide. Si el usuario lo necesita mañana, pide un enlace nuevo tras autenticar.
Paso a paso: construir un flujo de descarga seguro
Mantén tus archivos privados por defecto. Ponlos en un almacenamiento que no sea servido directamente por tu servidor web o CDN como una carpeta pública. Trata las descargas como una acción que realiza tu app, no como un archivo estático que cualquiera puede adivinar.
Aquí hay un flujo simple que funciona en aplicaciones reales:
- Almacena archivos de forma privada, con IDs: Guarda cada archivo con una clave interna aleatoria (o un ID en la base de datos), no con un nombre suministrado por el usuario como
../../secret.env. Guarda el nombre original solo como metadato para mostrar. - El usuario solicita una descarga: Llama a un endpoint como
GET /downloads/:fileIdmientras está autenticado. - Comprueba permisos primero: Busca el registro del archivo y confirma que el usuario actual puede acceder (propietario, miembro del equipo, plan pagado, lo que sean tus reglas).
- Genera una URL firmada de corta duración: Crea una URL que incluya
fileId, una marca de expiración y una firma (HMAC). Hazla válida por minutos, no por días. - Redirige o devuelve la URL firmada: El cliente solicita entonces la URL firmada para recibir los bytes.
Cuando el endpoint de la URL firmada recibe una petición, sé estricto antes de tocar cualquier dato de archivo. Verifica la firma y la expiración primero. Luego carga el archivo por su clave interna, nunca por una ruta cruda enviada por el cliente. Finalmente, transmite (stream) el archivo al usuario para que descargas grandes no llenen la memoria del servidor.
Una buena respuesta de descarga también fija cabeceras seguras. Fuerza la descarga con Content-Disposition: attachment y establece Content-Type desde metadatos de confianza (o un paso de detección en el servidor). No reflejes un content-type proporcionado por el usuario, porque así es como endpoints de “descarga” se convierten en páginas ejecutables en el navegador.
Para errores, sé útil pero no revelador. Di “No encontrado” o “Enlace expirado”, pero no incluyas rutas internas, nombres de buckets o trazas de pila.
Errores comunes que convierten las URLs firmadas en una falsa sensación de seguridad
Las URLs firmadas pueden ser muy seguras, pero solo si las tratas como una pieza dentro de una comprobación de acceso más amplia. La mayoría de fallos ocurren cuando la firma es correcta, pero el servidor aún sirve el archivo equivocado o lo hace de forma riesgosa.
Una trampa común es firmar la “cadena URL completa” exactamente como aparece en el navegador. Pequeños cambios pueden romper la validación o crear bypasses: parámetros de consulta reordenados, espacios codificados de distinta forma o un proxy que añada un parámetro. Si tu código firma una versión pero verifica otra, obtienes descargas intermitentes y soluciones “temporales” de emergencia que debilitan la seguridad.
Otro error frecuente es dejar que el usuario controle la ruta o la extensión del archivo. Incluso con una firma válida, no quieres ../ trucos, caracteres Unicode inesperados o subidas con extensión .html pasando. Una URL firmada debería apuntar a un identificador estable (por ejemplo un ID interno) y el servidor debería decidir la ruta real de almacenamiento.
La expiración es otro punto donde los buenos diseños fallan silenciosamente. Equipos generan enlaces “temporales” que duran semanas o olvidan comprobar la marca de tiempo. Eso convierte un archivo privado en un endpoint público de larga duración que puede compartirse, indexarse, reenviarse o rasparse.
Finalmente, muchas apps olvidan que las descargas también son una superficie de entrega de contenido. Si sirves subidas de usuarios como text/html (o permites que el navegador lo adivine), un archivo subido puede ejecutarse como página web. Eso puede llevar a compromisos de cuentas mediante inyección de scripts, aunque la URL estuviera firmada.
Señales de alarma a vigilar
Estos patrones aparecen frecuentemente en revisiones de código reales:
- Las firmas dependen del orden exacto de parámetros o de la codificación exacta de la URL.
- Los usuarios pueden solicitar rutas arbitrarias, nombres de archivo o extensiones.
- Falta expiración, no se aplica o se fija demasiado lejos en el futuro.
- Las respuestas permiten content sniffing o devuelven un tipo de contenido incorrecto.
- Secretos de firma aparecen en código cliente, logs o mensajes de error detallados.
Ejemplo: proteger descargas de facturas y documentos
Imagina un portal de clientes donde cada cuenta tiene facturas (PDF) y documentos de verificación de identidad (imágenes o PDFs). Esos archivos son privados por defecto, pero los usuarios necesitan un botón simple de “Descargar”.
Un modo de fallo común es que el portal genere una URL permanente como /files/1234/invoice.pdf. Alguien la reenvía en un chat grupal y ahora cualquiera que tenga el enlace puede obtenerla. Peor aún, motores de búsqueda y herramientas de logs pueden almacenarla, convirtiendo un documento privado en un endpoint semi-público.
Con descargas firmadas y que expiran, el portal evita exponer una dirección de archivo estable y adivinable. En su lugar, emite un enlace de corta duración vinculado al archivo exacto y (cuando procede) al usuario u organización actual.
Aquí hay un flujo práctico:
- El usuario pulsa “Descargar factura” estando autenticado.
- El servidor pregunta: ¿es este usuario el propietario de la factura
inv_9281? - El servidor genera una URL firmada que incluye
user_id,file_idy una expiración. - El endpoint de descarga verifica la firma y la expiración, luego obtiene el archivo por
file_iddesde el almacenamiento (no por una ruta que envíe el navegador). - La respuesta fuerza un tipo de contenido seguro y comportamiento de descarga (por ejemplo
application/pdfmás un nombre de archivo para descarga).
Las dos protecciones cuando ese enlace llega a un chat grupal son expiración y ámbito. La expiración limita la fuga en el tiempo. El ámbito hace que el servidor rechace la petición a menos que el usuario autenticado (u org) coincida con lo firmado. Si soportas accesos sin contraseña por email, puedes acotar al token de sesión o a un token de un solo uso. La idea es la misma: el enlace no es una llave universal.
Soporte eventualmente recibirá: “Mi enlace expiró.” No lo arregles haciendo que los enlaces duren una semana. Un patrón más seguro es que soporte reenvíe un enlace nuevo tras verificar al usuario (por ejemplo, autenticación o confirmación por email).
Lista de comprobación rápida y próximos pasos
Si quieres que las URLs firmadas realmente protejan archivos privados, las configuraciones más seguras son aburridas. Hacen las mismas comprobaciones cada vez y nunca confían en entradas que no crearon.
Usa esta lista para revisar tu flujo de descargas:
- Tu endpoint de descarga acepta un ID estable (como
file_id), no una ruta o nombre de archivo proporcionado por el usuario. - Validáis la firma y la expiración, y rechazáis parámetros de consulta inesperados (sin “ajustes” extra que un atacante pueda tocar).
- Fijas
Content-TypeyContent-Dispositiona propósito (por ejemplo, forzar descargas para documentos en vez de dejar que el navegador adivine). - Los archivos viven en almacenamiento privado, tu app usa credenciales de mínimo privilegio y los mensajes de error son vagos (sin pistas como “el archivo existe” ni trazas de pila).
- Registras fallos (firma mala, enlace expirado, acceso denegado) para detectar patrones sin filtrar detalles al usuario.
Una expectativa simple: si alguien copia un enlace válido desde su portátil y lo pega en otro navegador, o bien sigue siendo válido para el usuario previsto y la ventana de tiempo, o bien falla limpiamente. Nunca debe convertirse en un endpoint público permanente solo porque se compartió.
Próximos pasos
Si estás reforzando un sistema existente, empieza pequeño y avanza hacia fuera:
- Escoge un tipo de archivo sensible (facturas, contratos, exportaciones) y muévelo detrás de un endpoint de descarga firmado que use IDs.
- Añade validación estricta: firma, expiración y una lista blanca de parámetros permitidos. Luego añade cabeceras de tipo de contenido y de descarga.
- Haz una pasada rápida de abuso: prueba cadenas de traversal, añade parámetros de consulta aleatorios, modifica la expiración y confirma que cada intento falla de forma segura.
Si tu app fue generada por IA (de herramientas como Lovable, Bolt, v0, Cursor o Replit), vale la pena auditar las rutas de descarga en particular. FixMyMess (fixmymess.ai) se centra en diagnosticar y reparar problemas como autorización rota, secretos expuestos y manejadores de descarga inseguros, y ofrece una auditoría de código gratuita para mapear riesgos antes de hacer cambios.
Preguntas Frecuentes
¿Por qué mi “descarga privada” sigue funcionando cuando pego el enlace en otro navegador?
Una descarga “privada” se vuelve pública cuando la URL funciona por sí sola sin comprobar permisos cada vez que se solicita. Si alguien puede copiar, adivinar o automatizar esa URL y tu servidor sigue devolviendo el archivo, la descarga es efectivamente pública aunque el botón solo se mostrara a usuarios autenticados.
¿No basta con URLs largas y difíciles de adivinar para proteger las descargas?
Las rutas de aspecto aleatorio ayudan un poco, pero no son seguridad. Los enlaces se comparten, quedan en el historial del navegador, se capturan en logs, se pegan en chats de soporte o los descubre un escaneo automatizado. Una URL estable puede seguir funcionando para siempre si no aplicas autorización en el servidor.
¿Qué es exactamente una URL firmada en términos sencillos?
Una URL firmada es un enlace de descarga normal que incluye una firma que tu servidor puede verificar con una clave secreta. Si alguien modifica el ID del recurso, la expiración o el ámbito, la firma ya no coincide y la petición se rechaza antes de enviar bytes del archivo.
¿Qué debo firmar y cuál es la forma más segura de validarlo?
Firma un conjunto pequeño y estricto de campos como el ID del recurso, una marca de tiempo de expiración y, cuando haga falta, un ámbito de usuario u organización. Calcula un HMAC sobre una cadena canónica en un orden fijo. Al validar, rechaza campos faltantes o malformados, aplica la expiración, recalcula el HMAC y compara usando una comprobación en tiempo-constante.
¿Cuándo un endpoint de descarga se vuelve vulnerable a path traversal?
Se vuelve vulnerable cuando el cliente puede influir en la ruta del sistema de archivos, aunque sea de forma indirecta. Un patrón más seguro es aceptar un ID opaco de archivo, buscar la clave real de almacenamiento en el servidor, confirmar que quien solicita tiene permiso y únicamente entonces leer o transmitir el archivo.
¿Por qué importan las cabeceras de tipo de contenido si la URL está firmada?
Porque puedes servir un archivo subido como una página web. Forza comportamiento de descarga con Content-Disposition: attachment, establece un Content-Type de confianza y añade X-Content-Type-Options: nosniff para que el navegador no intente adivinar y renderizarlo como HTML o script.
¿Cuánto debe durar un enlace de descarga que expira?
Usa la ventana más corta que aún permita al usuario completar la acción. Muchas apps usan 5–15 minutos para ver un documento y más tiempo solo para exportaciones grandes; para todo lo demás, genera un enlace nuevo cuando el usuario vuelva a autenticarse.
¿Puede alguien compartir una URL firmada y permitir que otros descarguen el archivo?
Las URLs firmadas reducen el abuso casual, pero no impiden que alguien reenvíe un enlace válido mientras siga activo. Si necesitas control más estricto, incluye en la firma el userId/orgId o la sesión y sigue aplicando autorización normal en el servidor cuando se pida la descarga.
¿Cómo rotar las claves de firma sin romper enlaces activos?
Incluye un pequeño identificador de clave (kid) y mantén las claves antiguas disponibles solo por un breve periodo mientras expiran los enlaces existentes. Mantén la expiración corta para no tener que soportar claves antiguas mucho tiempo, y nunca incluyas secretos de firma en código cliente o logs verbosos.
Mi app fue generada por IA: ¿cuál es la forma más rápida de comprobar si las descargas son inseguras?
Muchas apps generadas por IA incluyen rutas de descarga que solo “ocultan” URLs, aceptan file=... en la URL, omiten validación estricta o devuelven cabeceras inseguras. Si heredas un prototipo con fugas de archivos o lógica de auth desordenada, FixMyMess puede hacer una auditoría de código gratuita y luego reparar el flujo de descarga, incluyendo comprobaciones de autorización, validación de URLs firmadas y cabeceras más seguras.