Loading...

Seguridad · 9 min de lectura

URLs firmadas: enlaces que dejan de funcionar

Una URL firmada lleva la prueba de que la emitiste tú y un momento en el que muere. Es el control que quieres para los archivos por los que la gente paga. Esta guía cubre las dos familias de URL firmada, la receta exacta en cdn.com.tr, y el error de caché que hace que cada descarga protegida golpee tu origen.

Actualizado

URLs firmadas: enlaces que dejan de funcionar

Qué promete en realidad una URL firmada

Tres cosas, y vale la pena ser preciso porque la gente espera una cuarta.

Prueba que el enlace vino de ti. La firma es un hash de la ruta, la caducidad y un secreto que solo conoce tu servidor. Nadie puede fabricar un enlace que funcione sin el secreto.

Caduca. La caducidad es parte de lo que se firma, así que no se puede editar sin romper la firma.

Se comprueba antes de tocar tu origen. En un edge de CDN, una petición sin firmar se rechaza en el edge; tu servidor nunca la ve.

Lo que no promete: que la persona que abrió el enlace no vaya a guardar el archivo y mandárselo por correo a un amigo. Una URL firmada controla el acceso a la URL, no a los bytes. Si necesitas control después de la descarga, lo que buscas es DRM, que es un producto distinto y mucho más pesado.

Dos familias: firmas en el edge y presigned URLs

Firmas de ruta en el edge — lo que te da un CDN. Tu aplicación calcula una firma para una ruta y una caducidad; el edge la verifica al llegar. El archivo puede vivir en cualquier lugar al que el CDN pueda llegar, y la comprobación ocurre cerca del visitante.

Presigned URLs del almacenamiento de objetos — lo que te dan S3 y el almacenamiento compatible con S3. El propio servicio de almacenamiento emite una URL temporal, firmada con tu clave de acceso, normalmente válida por minutos. La firma viaja en la cadena de consulta como X-Amz-Signature y compañía.

No son competidoras. Un arreglo común y sensato es almacenamiento de objetos detrás de un CDN, con la firma del edge protegiendo la URL pública y el bucket mantenido privado para que nadie pueda saltarse el edge. Lo que no deberías hacer es exponer directamente al público una URL de almacenamiento presigned y asumir que el CDN la está protegiendo — si el enlace apunta al bucket, el edge no está en el trayecto en absoluto.

Cómo funciona en cdn.com.tr

Es un ajuste en una regla de entrega, así que puedes proteger /downloads mientras el resto del sitio sigue siendo público. Activa los enlaces que caducan para esa regla, define un secreto, y genera enlaces en tu aplicación.

La firma es la forma base64url del MD5 en crudo de tres cosas unidas: la caducidad, la ruta, y el secreto con un único espacio delante.

``` $uri = "/downloads/report.pdf"; $expires = time() + 600; // ten minutes $secret = "your-long-random-secret";

$token = rtrim(strtr(base64_encode( md5($expires . $uri . " " . $secret, true) ), "+/", "-_"), "=");

$url = "https://cdn.example.com{$uri}?md5={$token}&expires={$expires}"; ```

Tres resultados, y son deliberadamente distintos para que tus registros puedan diferenciarlos:

| Petición | Respuesta | |---|---| | Firma válida, no caducada | el archivo, cacheado con normalidad | | Sin firma, o una equivocada | 403 | | Firma correcta, caducidad pasada | 410 |

El 410 importa más de lo que parece. Cuando un cliente dice "tu enlace está roto", el código de estado por sí solo te dice si le enviaron un enlace malo o simplemente esperó demasiado — sin tener que preguntarle nada.

La trampa de caché que te cuesta el CDN

Esta es la parte que falta en la mayoría de la documentación, y es la que duele.

Cada enlace firmado es único: un md5 distinto y un expires distinto para cada visitante y cada emisión. Si tu clave de caché incluye la cadena de consulta — que es lo habitual por defecto, y es lo nuestro a menos que lo cambies — entonces cada petición firmada es una entrada de caché distinta. La tasa de aciertos para los archivos protegidos cae a cero, cada descarga se obtiene de tu origen, y has pagado por un CDN que está actuando como un proxy.

La solución es un solo ajuste. En la regla de entrega, configura el modo de cadena de consulta para que los parámetros de la firma no entren en la clave de caché:

- Lista de ignorados — ignora md5 y expires, conserva cualquier otra cosa relevante. Es la elección correcta cuando el mismo archivo también se pide con parámetros reales. - Ignorar todos — la opción más simple cuando las rutas protegidas no llevan ningún parámetro relevante, lo cual suele ser cierto en las descargas.

Compruébalo después en lugar de asumirlo: pide el mismo archivo dos veces con dos firmas válidas distintas y mira la cabecera de estado de caché. La segunda debería ser un acierto. Si las dos son fallos, la clave todavía contiene la firma.

Elegir una caducidad

Lo bastante corta como para que un enlace filtrado no valga nada; lo bastante larga como para que la descarga termine en una conexión mala.

Documentos e imágenes: de cinco a quince minutos. Se piden inmediatamente después del clic.

Vídeo grande y archivos comprimidos: de una a seis horas. Un archivo de dos gigabytes en una conexión móvil lenta tarda más de lo que la gente espera, y una caducidad que se dispara a mitad de la descarga genera un ticket de soporte que parece corrupción.

Los manifiestos de streaming necesitan cuidado. Con HLS, el reproductor pide el manifiesto y luego muchos segmentos a lo largo de la reproducción. Si firmas cada segmento con una caducidad corta, la reproducción se rompe a mitad de un vídeo largo. Firma con una caducidad que cubra toda la sesión, o firma el manifiesto y deja los segmentos protegidos por otro control.

Una cosa que no puedes hacer es revocar un único enlace emitido. Es válido hasta su caducidad, y punto. Si necesitas revocación inmediata, rota el secreto — lo cual invalida todos los enlaces que has emitido.

Los errores que filtran el secreto

Firmar en el navegador. Si el secreto está en JavaScript, es público. Firmar es cosa del servidor, siempre. Suena obvio y es la forma más común, con diferencia, en la que se escapan los secretos.

Incrustar enlaces en tiempo de build. Un enlace generado durante un build estático lleva una caducidad fijada en el momento del build, y caduca mientras la página sigue publicada. Genera en el momento de la petición, o a través de un pequeño endpoint que redirige.

Desfase de reloj. La caducidad se compara contra el reloj del edge. Si tu servidor de aplicación va varios minutos atrasado, los enlaces nacen caducados. Mantén NTP funcionando; cuando un enlace falla inmediatamente después de emitirse, comprueba el reloj antes que el código.

Poner el secreto en un repositorio. Trátalo como una credencial: variable de entorno, no control de versiones. Cuando alguien deja el equipo, rótalo.

Olvidar que la rotación es global. Cambiar el secreto invalida de golpe todos los enlaces pendientes, incluidos los que van en correos enviados hace cinco minutos. Eso es exactamente lo que quieres durante un incidente y exactamente lo que no quieres un viernes por la tarde por accidente.

Preguntas frecuentes sobre URLs firmadas

¿Cuál es la diferencia entre una URL firmada y una presigned URL?

Sobre todo, dónde ocurre la comprobación. "Presigned URL" es el término de S3 para un enlace temporal emitido por el servicio de almacenamiento y verificado por él. Una URL firmada en un edge de CDN se verifica en el edge, cerca del visitante, y funciona para cualquier origen y no solo para un bucket. El concepto es el mismo: una firma más una caducidad en la cadena de consulta.

¿Puede alguien compartir un enlace firmado?

Sí, hasta que caduca. Una firma demuestra que el enlace lo emitiste tú; no dice nada sobre quién lo tiene. Las caducidades cortas limitan el daño. Si necesitas el enlace atado a una persona, incluye algo identificativo en la ruta firmada y compruébalo por tu lado, y acepta que alguien decidido a compartirlo igualmente puede reenviar el archivo ya descargado.

¿Firmar perjudica la caché?

Solo si dejas que la firma entre en la clave de caché, y entonces perjudica mucho — cada enlace único se convierte en su propia entrada de caché y cada descarga va a tu origen. Configura el modo de cadena de consulta para ignorar los parámetros de la firma y el archivo se cachea con normalidad, compartido entre todos los que tengan un enlace válido.

¿Puedo revocar un solo enlace?

No de forma individual. Una firma es válida hasta su caducidad porque la verificación no guarda estado — no hay ninguna lista de la que eliminarla. Los controles disponibles son una caducidad corta y rotar el secreto, lo cual invalida todo de golpe.

¿No está roto el MD5 para esto?

El MD5 está roto para la resistencia a colisiones, que es lo que importa cuando un atacante puede elegir los dos mensajes. Aquí el atacante tiene que forjar el hash de una cadena que contiene un secreto que no tiene, que es un problema de preimagen y no de colisión. El riesgo práctico es que el secreto se filtre o sea adivinable — usa uno largo y aleatorio, mantenlo en el servidor, y rótalo.

¿Debería usar esto para vídeo HLS?

Funciona, con la salvedad de la duración de la sesión: la caducidad tiene que cubrir toda la reproducción, no solo la petición del manifiesto, o la reproducción se detiene a mitad de camino. Para contenido largo, combina una caducidad generosa con rate limiting en lugar de una caducidad muy corta que rompe el reproductor.