Cada job de GitHub Actions recibe un token. GitHub crea un GITHUB_TOKEN nuevo al inicio de cada job, limitado al repositorio en el que está el workflow, y cualquier paso del job puede usarlo. Lo que puede hacer depende de ajustes que muchos equipos nunca han mirado, y en los repositorios antiguos la respuesta sigue siendo «escribir en casi todo».

Eso importa cuando algo del job se vuelve malicioso. En el compromiso «s1ngularity» de Nx de agosto de 2025, el token del repositorio aún tenía permisos de lectura y escritura. Una inyección en un workflow pull_request_target lo utilizó para activar el workflow de publicación, que filtró el token de npm con el que se publicaron versiones maliciosas de Nx. Un token de solo lectura habría detenido ese paso.

Este artículo explica cómo se definen los permisos del token, qué necesita cada tarea habitual y cómo encontrar el mínimo para sus workflows.

Qué es GITHUB_TOKEN

GITHUB_TOKEN es un token de acceso de instalación de la app GitHub Actions. Algunas de sus propiedades pasan fácilmente desapercibidas:

  • Existe tanto si lo usa como si no. Los workflows hacen referencia a él como secrets.GITHUB_TOKEN, pero cualquier acción puede leerlo a través del contexto github.token sin que se le pase.
  • Se escribe en disco por defecto. actions/checkout guarda el token para comandos git posteriores salvo que establezca persist-credentials: false. Antes de la v6 iba a .git/config; la v6 lo trasladó a un archivo independiente bajo $RUNNER_TEMP. En 2024, la investigación ArtiPACKED de Unit 42 encontró tokens filtrados a través de artefactos que incluían .git/config.
  • Caduca con el job. En los runners alojados en GitHub eso significa 6 horas como máximo, el límite de tiempo del job. En los runners autoalojados se puede renovar hasta 24 horas.

Como el token siempre está ahí, lo único que usted controla es cuánto puede hacer.

El valor por defecto: permissive o restricted

Cada empresa, organización y repositorio tiene un valor por defecto para el token:

  • Permissive: lectura y escritura en todos los permisos.
  • Restricted: solo lectura en contents y packages, y nada más.

Desde el 2 de febrero de 2023, las empresas, organizaciones y repositorios nuevos usan restricted por defecto. El cambio no afectó a los existentes, así que los repositorios y organizaciones creados antes de esa fecha mantienen el valor permissive salvo que alguien lo cambiara. Si un nivel superior elige restricted, los niveles inferiores no pueden elegir permissive.

Compruébelo en Settings → Actions → General → Workflow permissions, primero a nivel de organización. La misma sección incluye Allow GitHub Actions to create and approve pull requests, que debe permanecer desactivado salvo que un workflow lo necesite.

La clave permissions

La clave permissions de un workflow ajusta el token respecto al valor por defecto. Funciona en dos niveles:

permissions:
  contents: read

jobs:
  release:
    permissions:
      contents: write
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@<full-commit-sha>

Las reglas:

  • El nivel de job prevalece sobre el de workflow. Establezca el valor por defecto del workflow en solo lectura y eleve los permisos únicamente en los jobs que los necesiten.
  • Todo lo que no incluya pasa a ser none. En cuanto define un permiso, se eliminan todos los demás. permissions: {} los elimina todos.
  • Los valores son read, write o none. write incluye read. read-all y write-all lo establecen todo a la vez; evite write-all.

Los permisos disponibles, a septiembre de 2026:

PermisoControla
actionsEjecuciones de workflows, artefactos y cachés
artifact-metadataRegistros de metadatos de artefactos (sustituyó a contents para estas API en 2026)
attestationsAtestaciones de artefactos
checksCheck runs y check suites
code-qualityResultados de calidad del código, como informes de cobertura
contentsContenido del repositorio, commits, ramas, etiquetas y releases
deploymentsDespliegues
discussionsDiscussions
id-tokenSolicitud de un token OIDC (solo write o none)
issuesIssues y comentarios
packagesGitHub Packages, incluido el registro de contenedores
pagesBuilds de GitHub Pages
pull-requestsPull requests, etiquetas y revisiones
security-eventsAlertas de code scanning y subidas de SARIF
statusesEstados de commits
vulnerability-alertsAlertas de Dependabot (solo read o none)

Cuando a un job le falta un permiso que necesita, la llamada a la API falla con 403 Resource not accessible by integration. Ese error es la señal para añadir el único permiso que necesita el paso, no para cambiar a write-all.

Qué necesitan las tareas habituales

TareaPermisos
Hacer checkout del códigocontents: read
Crear una release o subir una etiquetacontents: write
Comentar en una issue o pull requestissues: write (los comentarios de pull requests pasan por la API de issues)
Añadir etiquetas o revisiones a una pull requestpull-requests: write
Autenticarse en un proveedor de nube con OIDCid-token: write
Crear atestaciones de artefactosid-token: write, attestations: write, contents: read, además de packages: write para imágenes de contenedor
Subir una imagen a GitHub Container Registrypackages: write
Subir SARIF a code scanningsecurity-events: write
Desplegar GitHub Pages desde un workflowpages: write, id-token: write

La mayoría de los jobs de test y de build no necesitan nada más que contents: read.

Forks, pull_request_target y Dependabot

  • Las pull requests desde forks reciben un token de solo lectura en el evento pull_request, pida lo que pida el workflow, salvo que un administrador active «Send write tokens to workflows from pull requests». Déjelo desactivado.
  • pull_request_target recibe un token de lectura y escritura incluso para pull requests de forks públicos. Por eso está detrás de muchos de los robos de tokens más conocidos: el compromiso de Nx, el de Ultralytics en diciembre de 2024 y el de la acción de Trivy en marzo de 2026 empezaron todos con un workflow pull_request_target. GitHub lo está restringiendo: desde julio de 2026, actions/checkout se niega por defecto a hacer checkout del código de un fork en estos workflows y, a partir del 2 de noviembre de 2026, los repositorios públicos sin política propia tendrán pull_request_target desactivado por defecto.
  • Las pull requests de Dependabot se tratan como forks: un token de solo lectura y ningún secreto de Actions. Si está valorando otras herramientas de actualización, consulte alternativas a Dependabot.

Cómo encontrar los permisos mínimos

No hace falta averiguarlo leyendo el código fuente de cada acción:

  • actions-permissions de GitHub Security Lab monitoriza cómo usa un workflow su token y recomienda los permisos mínimos. Está en beta pública.
  • La comprobación Token-Permissions de OpenSSF Scorecard señala los workflows que no definen permisos de solo lectura en el nivel superior. Clasifica la comprobación como de alto riesgo.
  • zizmor, un analizador estático de workflows, señala los permisos excesivos o por defecto y los checkouts que conservan las credenciales.
  • Secure-Repo y Harden-Runner de StepSecurity recomiendan permisos a nivel de job a partir de una base de conocimiento de acciones y de las llamadas a la API que hace un job.

Un despliegue práctico: establezca el valor por defecto de la organización en restricted, añada permissions: contents: read al principio de cada workflow y ejecute los workflows. Los que fallen con Resource not accessible by integration le dirán exactamente qué job necesita qué permiso adicional.

Dónde encaja CRACI

El mínimo privilegio limita lo que puede hacer un token robado. No impide que lo roben, y los demás secretos del job, como los tokens de npm y las credenciales de la nube, están igual de expuestos.

CRACI ejecuta sus jobs de GitHub Actions en sus propios runners. Usted cambia runs-on a craci; GITHUB_TOKEN, la clave permissions y los ajustes de su repositorio funcionan como antes, porque GitHub sigue emitiendo el token. Lo que CRACI añade está en el runner: una política de egress que decide a qué hosts puede llegar un job, se valida antes de que empiece el job y, si falla, bloquea, y un registro de todos los paquetes que descargó el job, con una traza de sus conexiones de red. Si un código malicioso de un job llega a hacerse con un token, la política limita adónde puede enviarlo, y el registro muestra lo que ocurrió.

Para el resto de los secretos del job, consulte secretos de GitHub Actions: cómo se filtran y cómo protegerlos. Reserve una demo para ver el registro de uno de sus propios builds.