Provenance: procedencia firmada y SLSA desde CI
La procedencia firmada como respuesta criptográfica a los ataques a la cadena de suministro: cómo npm publish --provenance ata un tarball a su commit y su workflow con Sigstore y OIDC, qué garantiza el marco SLSA y sus niveles, cómo verificarla del lado del consumidor, y por qué el trusted publishing sin tokens es el nuevo patrón de referencia.
Cuando instalas un paquete, ¿cómo sabes que el tarball del registro se construyó de verdad a partir del código fuente que ves en el repositorio, y no lo inyectó un atacante que robó la cuenta del mantenedor? Durante décadas la respuesta fue “confía en la persona”. La procedencia firmada cambia la pregunta por otra, verificable: “confía en la prueba criptográfica de que este artefacto salió de este commit, en este workflow”. Es el eslabón que blinda la cadena de suministro.
- Situar los ataques a la cadena de suministro y por qué la confianza en cuentas es insuficiente.
- Entender cómo
npm publish --provenancefirma una atestación con Sigstore y OIDC sin claves de larga vida. - Relacionar la procedencia con los niveles del marco SLSA y con la insignia de verificado.
- Verificar firmas y atestaciones del lado del consumidor con
npm audit signatures.
El problema: la cadena de suministro
Los ataques más rentables ya no explotan tu código, sino tus dependencias. Comprometer un paquete popular es infectar de golpe cada proyecto que lo instala, un efecto multiplicador que ningún ataque directo alcanza.
Como el tarball se sube “a mano” desde un portátil, nada ata lo publicado a un código fuente auditable: el registro solo sabe que alguien con credenciales válidas subió unos bytes. El patrón se repite con variantes:
- Cuenta comprometida: roban el token o las credenciales del mantenedor y publican en su nombre.
- Máquina comprometida: el portátil del mantenedor inyecta código en la build local antes de subir.
- Dependencia infiltrada: una versión maliciosa de un paquete popular se cuela en miles de árboles a la vez.
La procedencia ataca la raíz del problema. En lugar de confiar en quién publica, exige una prueba verificable de cómo se construyó el artefacto: de qué commit exacto salió, en qué repositorio, con qué workflow de CI.
Si esa prueba es criptográficamente sólida y pública, un atacante que suba un tarball desde su portátil no puede fabricarla, porque no tiene el commit firmado ni la identidad del workflow legítimo.
La confianza se desplaza así de la persona al proceso: de “creo en el mantenedor” a “puedo verificar el origen”, un cambio de eje que lo reordena todo.
Cómo funciona la procedencia
El mecanismo es npm publish --provenance, ejecutado desde CI. Al publicar, npm genera una atestación de procedencia: un documento que ata el tarball a su commit de origen y a las instrucciones de build.
Lo firma usando Sigstore con una técnica llamada keyless: no hay clave privada que guardar ni rotar, y ahí reside buena parte de su seguridad.
En su lugar, el runner presenta su identidad OIDC a Fulcio, que le emite un certificado efímero; con él se firma la atestación, y el acto queda anotado en Rekor, un log de transparencia público e inmutable. Sin secretos de larga vida que robar, se elimina una clase entera de ataques.
OIDC
La identidad efímera del workflow de CI. Sustituye al token robable: el registro confía en quién ejecuta, no en un secreto guardado.
Fulcio
La autoridad de Sigstore que emite un certificado de vida cortísima para firmar sin custodiar claves privadas.
Rekor
El log de transparencia público donde queda anotada cada firma, para que cualquiera pueda auditarla después.
flowchart LR CI[CI con identidad OIDC] --> F[Fulcio emite certificado efimero] F --> S[firma la atestacion de procedencia] S --> R[Rekor log de transparencia] S --> N[registro npm] N --> B[insignia verificado en npmjs] style CI fill:#89b4fa,color:#11111b style S fill:#cba6f7,color:#11111b style B fill:#a6e3a1,color:#11111b
La firma keyless impone requisitos concretos, y cada uno tiene su razón:
- CI compatible: GitHub Actions o GitLab CI, capaces de emitir tokens OIDC.
- Permiso
id-token: write: sin él no hay identidad que presentar a Fulcio. - Paquete y repositorio públicos: la procedencia solo tiene sentido si cualquiera puede contrastar el artefacto contra un código visible.
# .github/workflows/publish.yml
permissions:
id-token: write # habilita OIDC y la firma keyless de Sigstore
contents: read
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
registry-url: https://registry.npmjs.org
- run: npm ci
- run: npm publish --provenance --access public
El error más común al estrenar procedencia es un workflow que publica pero rechaza firmar, porque olvidaste declarar id-token: write en los permissions. Sin ese permiso, el runner no puede presentar su identidad OIDC a Fulcio y la firma no ocurre. El segundo tropiezo habitual es intentar procedencia sobre un repositorio privado: hoy la atestación exige un origen público, porque su valor entero reside en que cualquiera pueda contrastar el artefacto contra el código.
SLSA, la insignia y el trusted publishing
Toda esta maquinaria encaja en un marco con nombre: SLSA, las siglas de niveles de cadena de suministro para artefactos de software. SLSA define una escalera de garantías de build ascendentes:
- Nivel 1: existe una procedencia que documenta el origen, aunque la genere el propio proceso.
- Nivel 2: la procedencia la firma un servicio de build alojado, no el publicador; deja de ser falsificable a mano.
- Nivel 3: el build corre aislado y endurecido, con garantías más fuertes contra manipulación.
La atestación de procedencia de npm, generada por un CI alojado y firmada con Sigstore, cubre los niveles intermedios de esa escalera.
Cuando la firma llega al registro, npmjs muestra la insignia de verificado, que enlaza a evidencia contrastable:
- El commit exacto del que salió el tarball.
- El repositorio de origen y el workflow que lo construyó.
- La entrada en Rekor que prueba que la firma es auténtica.
El patrón de referencia en 2026 suma una pieza más: el trusted publishing. En vez de guardar un token de publicación de larga vida como secreto en CI —otro objeto robable—, el registro confía directamente en la identidad OIDC del workflow para autorizar la publicación. Sin token que filtrar y con procedencia firmada, se cierran a la vez las dos puertas clásicas del atacante: las credenciales robadas y los artefactos sin origen.
La procedencia responde a “de dónde salió este tarball”; el trusted publishing responde a “quién tuvo permiso de subirlo”. Son mecanismos distintos —uno firma el artefacto, el otro sustituye el token de autenticación— pero apuntan a la misma clase de ataque. Adoptados juntos desde CI, no queda ni un secreto de larga vida que robar ni un artefacto sin origen contrastable. Es la razón por la que el ecosistema los promueve como pareja, no por separado.
Verificar del lado del consumidor
La procedencia no sirve de nada si nadie la comprueba. Del lado de quien instala, npm audit signatures recorre el árbol instalado y verifica dos cosas distintas.
Comprueba que cada tarball lleve la firma del registro que garantiza que no se alteró en tránsito, y que las dependencias con procedencia traigan una atestación válida que las ate a su origen. Un fallo aquí es una señal roja: algo en la cadena no cuadra.
# verifica firmas del registro y atestaciones de procedencia de tus dependencias
npm audit signatures
El informe distingue tres situaciones que conviene saber leer:
- Firma y procedencia válidas: la dependencia viene, comprobadamente, del origen que declara.
- Firma válida sin procedencia: íntegra en tránsito, pero sin prueba de build; aún es lo común en buena parte del ecosistema.
- Firma inválida o ausente: la señal de alarma; detén el despliegue e investiga antes de seguir.
Integrar esta comprobación en CI convierte la confianza en una puerta automática: si una dependencia pierde su firma o su procedencia no valida, el pipeline se detiene antes de desplegar. La verificación deja de ser un gesto puntual y pasa a ser una condición estructural del despliegue.
El salto conceptual de este nivel es de los más profundos de todo el track: la seguridad de la cadena de suministro deja de ser una cuestión de reputación para volverse una cuestión de prueba. Antes, instalar un paquete era un acto de fe en que la cuenta del mantenedor no estuviera comprometida; una fe que los ataques reales han traicionado una y otra vez. La procedencia sustituye esa fe por una cadena verificable: la identidad OIDC del workflow, atestiguada por Fulcio con un certificado efímero, firmando una atestación que ata el tarball a un commit público, registrada para siempre en el log de transparencia de Rekor. Cada eslabón elimina un secreto robable o introduce una prueba contrastable. SLSA le pone nombre y niveles a esa escalera de garantías, y el trusted publishing remata la obra quitando de en medio el último objeto peligroso, el token de larga vida. Del otro lado, npm audit signatures cierra el círculo permitiendo que el consumidor no confíe, sino verifique. El resultado no es “más seguridad” en abstracto, sino un cambio de modelo mental: ya no preguntas en quién confías, sino qué puedes verificar. Cuando adoptas procedencia y publicación sin tokens desde CI, tu paquete deja de pedirle fe al mundo y empieza a ofrecerle evidencia. Ese es el estándar que el ecosistema ha decidido volver la norma, y saber operarlo es hoy parte del oficio de publicar.
- Explica con tus palabras qué clase de ataque a la cadena de suministro neutraliza la procedencia y cuál no.
- Escribe un workflow de CI con el permiso
id-token: writeque publique con--provenance. - Localiza en un paquete real de npmjs la insignia de verificado y sigue el enlace al commit y al workflow de origen.
- Corre
npm audit signaturesen un proyecto y lee cuántas dependencias traen firma y procedencia. - Investiga el trusted publishing sin tokens y razona qué ventaja añade sobre guardar un token en el CI.