ApostropheCMS uploadfs con backend Azure filtra el token SAS en cada URL pública de adjuntos
Resumen
Cuando uploadfs se configura con el backend de Azure y sas: true (una configuración compatible introducida en uploadfs 1.25.0), createContainer() construye el BlobServiceClient a partir de una URL que incrusta la cadena de consulta (query string) SAS completa, y los clientes derivados del SDK v12 de Azure heredan dicha cadena de consulta. Como resultado, getUrl() devuelve una URL base que incluye la firma SAS completa de la cuenta/contenedor (?sv=...&sig=...), y attachment.url() de Apostrophe la antepone a cada enlace público de archivos adjuntos, exponiendo una credencial SAS a nivel de contenedor (comúnmente sp=rwdlac, acceso total de lectura/escritura/eliminación/listado) a todos los visitantes del sitio, incluidos los anónimos. Cualquiera que pueda ver la URL de un adjunto puede leer, sobrescribir, eliminar y listar todos los blobs del contenedor durante la vigencia del token SAS. Un error funcional complementario (la ruta del blob se añade después de la cadena de consulta, generando URLs corruptas) evidencia que esta ruta de código nunca se ejecutó de extremo a extremo. El modo de clave compartida (predeterminado) y los backends que no son de Azure no están afectados.
Verificado en el commit 5a3746aaed49761e171c2cbfe793267c959829fd (uploadfs 1.27.0 en el monorepositorio de apostrophe).
Detalles
Causa raíz: se reutiliza una URL de cliente que contiene credenciales como la URL base pública. La cadena:
- Cliente con credenciales incrustadas: azure.js#L123-L125 —
new BlobServiceClient("https://<account>.blob.core.windows.net?<SAS query string>")dondecluster.keyes la cadena SAS completa que incluyesig - Punto de filtración: azure.js#L513-L520 —
getUrl()tomagetContainerClient(...).getBlobClient('').urlcomobaseUrl; los clientes derivados de SDK v12 heredan la cadena de consulta SAS, por lo quebaseUrlya contiene?sv=...&sig=... - Exposición pública: attachment/index.js#L694 —
attachment.url()=uploadfs.getUrl() + pathpara cada enlace de archivo adjunto renderizado; attachment/index.js#L1503 —getBrowserData()también envíauploadsUrla cada sesión de edición iniciada - Error complementario: la ruta del blob se agrega después de la cadena de consulta (
/container/?sv=...&sig=XXX/attachments/id-name), por lo que estas URLs también quedan rotas, lo que prueba que la ruta de código 1.25.0 nunca se probó
Superficie de ataque: toda página pública que renderice URLs de archivos adjuntos (imágenes, enlaces de descarga), además de la uploadsUrl entregada a las sesiones de los editores. La fuga es pasiva e incondicional: no se requiere ninguna acción por parte de la víctima más allá de servir una página. La credencial se propaga adicionalmente a través del código fuente HTML, registros, cabeceras Referer y registros de acceso de proxies o CDN.
Mitigación sugerida:
- Eliminar la cadena de consulta antes de construir URLs públicas:
const u = new URL(blob.svc.getContainerClient(...).getBlobClient('').url); u.search = '';(o componeraccountUrl + '/' + containermanualmente) y añadir la ruta del blob al pathname, no después de la consulta. - Documentar que el modo SAS debe utilizar el privilegio mínimo (solo lectura, idealmente
sr=bpor blob), y priorizar la generación de URLs firmadas de solo lectura y de corta duración por blob a través degenerateBlobSASQueryParametersen lugar de incrustar un SAS a nivel de servicio en URLs públicas permanentes.
PoC
PoC completo publicado como el primer comentario abajo (poc.js + README; sin conexión, no se requiere cuenta de Azure). Replica con exactitud los dos pasos de la biblioteca con la dependencia declarada @azure/storage-blob@12:
npm install @azure/storage-blob@12 && node poc.js- El script construye
new BlobServiceClient("https://myaccount.blob.core.windows.net?sv=...&sig=FAKESIGNATURE")exactamente comocreateContainer(), luego evalúa la lógica degetUrl()(getContainerClient('uploads-container').getBlobClient('').url) y valida que la URL base resultante todavía contiene el parámetrosig. - Salida observada (código de salida 0):
baseUrl: https://myaccount.blob.core.windows.net/uploads-container/?sv=2021-06-08&ss=b&srt=co&sp=rwdlac&se=2099-01-01&sig=FAKESIGNATURE
attachment URL as served to visitors: https://myaccount.blob.core.windows.net/uploads-container/?sv=2021-06-08&ss=b&srt=co&sp=rwdlac&se=2099-01-01&sig=FAKESIGNATURE/attachments/abc123-photo.jpg
La URL generada filtra el token (sv…sig) y evidencia la ubicación errónea de la ruta. La comprobación cruzada de attachment/index.js confirma que esta URL llega intacta a las páginas públicas.
Impacto
- Qué es: credenciales insuficientemente protegidas (CWE-522) que dan lugar a exposición de información (CWE-200). Un token SAS a nivel de contenedor —al cual se le suele otorgar permisos de
sp=rwdlac(lectura/escritura/eliminación/listado/ACL)— queda expuesto a cualquier visitante del sitio, incluidos usuarios anónimos, con validez durante toda la vigencia del SAS (a menudo años). Quienes posean el token pueden leer, sobrescribir, eliminar y enumerar todos los archivos adjuntos en el contenedor; la biblioteca no aplica ninguna restricción. - A quién afecta: sitios Apostrophe que utilizan uploadfs con el backend de Azure y
sas: true(introducido en 1.25.0; presente hasta 1.27.0). El modo predeterminado de clave compartida y otros backends no se ven afectados. Cuando el token SAS se aprovisiona únicamente con permisos de lectura, el impacto se reduce a la lectura o descarga no autenticada de todos los archivos adjuntos.
Reportado por Kimi Security Team (bug-report@moonshot.ai).
Qué hacer
Actualice a uploadfs versión 1.28.0 o posterior.
Software afectado
Haz clic en una tecnología para ver todas sus vulnerabilidades.
| Plataforma | Paquete | Versiones vulnerables | Corregida en |
|---|---|---|---|
| Paquete de npm | uploadfs | >= 1.25.0, < 1.28.0 | 1.28.0 |
Referencias
- https://github.com/apostrophecms/apostrophe/security/advisories/GHSA-hhph-8536-gfq5
- https://github.com/apostrophecms/apostrophe/commit/9500b8b0e1d630d5e07fbb5fd2c25ed0943845ce
- https://github.com/apostrophecms/apostrophe/commit/f84fd41cbf6c22eb8d0008fc9c25df1f7130b350
- https://github.com/apostrophecms/apostrophe
- https://github.com/apostrophecms/apostrophe/releases/tag/uploadfs@1.28.0
- Vector CVSS
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L- Debilidad (CWE)
CWE-200, CWE-522- Plataforma
- npm
- Paquete
uploadfs- Publicada
- 20.10.2026
- Actualizado
- 20.10.2026 21:30
Fuentes y licencias · Editado o traducido por DockRay; se mantienen los derechos y las licencias de las fuentes.
OSV / GitHub Advisory Database - GHSA-hhph-8536-gfq5 - CC BY 4.0
Historial de vulnerabilidades: uploadfs
Todas las vulnerabilidades publicadas de este paquete (Paquete de npm), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.
Esta es la única vulnerabilidad conocida de este paquete.
Identifica las vulnerabilidades de tu aplicación
Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.