Auditoría de Workflows de GitHub para Detectar Riesgos de Seguridad
Los ataques a la cadena de suministro dirigidos a GitHub Actions han dejado de ser teóricos para convertirse en algo habitual. El compromiso de tj-actions/changed-files en marzo de 2025 demostró con qué rapidez una sola acción comprometida puede filtrar secretos en miles de repositorios. Un año después, el patrón se repitió con el incidente de Trivy-action, donde los atacantes forzaron la inserción de código malicioso en 76 de los 77 tags de versión.
Si ya tienes workflows ejecutándose en producción, esta lista de verificación te ayuda a identificar las debilidades más comunes sin necesidad de rediseñar toda tu pipeline.
Puntos Clave
- Establece
permissions: {}a nivel de workflow y concede únicamente los permisos mínimos que cada job necesita. - Nunca interpolés valores de
${{ github.event.* }}directamente en comandos de shell; en su lugar, pásalos a través de variables de entorno. - Fija las acciones de terceros a SHAs de commit completos y evita combinar
pull_request_targetcon checkouts de código de forks. - Reemplaza las credenciales estáticas de la nube con OIDC y evita que los runners auto-hospedados ejecuten código no confiable en repositorios públicos.
Verifica Primero los Permisos de GITHUB_TOKEN
Abre cualquier archivo de workflow y busca el bloque permissions de nivel superior. Si no está presente, se aplicará el valor predeterminado de tu organización, y las organizaciones creadas antes de febrero de 2023 suelen tener permisos de lectura y escritura por defecto.
La solución es sencilla:
permissions: {} # deny all at workflow level
jobs:
build:
permissions:
contents: read # grant only what the job needs
Establecer permissions: {} a nivel de workflow te obliga a declarar exactamente qué necesita cada job. Un job que solo lee código nunca debería tener un GITHUB_TOKEN con acceso de escritura a tu repositorio.
Busca Entradas No Confiables en Comandos de Shell
Busca en tus archivos de workflow la cadena ${{ github.event dentro de bloques run:. Este es el patrón de inyección de scripts más frecuente:
# Risky: attacker controls the PR title
- run: echo "Checking ${{ github.event.pull_request.title }}"
La alternativa segura consiste en pasar los valores no confiables a través de una variable de entorno intermedia:
- name: Check PR title
env:
TITLE: ${{ github.event.pull_request.title }}
run: echo "Checking $TITLE"
El valor se pasa mediante el entorno en lugar de interpolarse directamente en el script de shell, por lo que los metacaracteres del shell presentes en la entrada no pueden escapar y ejecutar comandos. También presta atención a las escrituras en GITHUB_ENV y GITHUB_PATH en pasos que manejan contenido controlado por el usuario; los atacantes pueden aprovecharlos para inyectar variables de entorno o binarios maliciosos en pasos posteriores.
Audita el Uso de pull_request_target
pull_request_target se ejecuta con acceso a los secretos de la rama base, lo que lo hace útil para workflows que necesitan comentar en PRs provenientes de forks. El riesgo no está en el trigger en sí, sino en combinarlo con un checkout del código del fork:
# Dangerous combination
on: pull_request_target
jobs:
test:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }} # runs attacker code
- run: npm test # with access to your secrets
Si utilizas pull_request_target, asegúrate de que ningún paso ejecute código proveniente de la rama del PR. GitHub mitigó parcialmente este problema a finales de 2025, pero el trigger sigue siendo de alto riesgo cuando se combina con checkouts de forks.
Discover how at OpenReplay.com.
Revisa el Pinning de Acciones de Terceros
El compromiso de tj-actions/changed-files fue posible porque los equipos referenciaban tags mutables como @v35. Los valores de los tags pueden ser reescritos de forma silenciosa. El pinning a un SHA de commit completo previene esto:
# Vulnerable
- uses: tj-actions/changed-files@v35
# Safe
- uses: tj-actions/changed-files@d6babd6899969df1a11d14c368283ea4436bca78
GitHub ofrece ahora políticas a nivel de organización para exigir el pinning por SHA y hacer fallar los workflows que utilicen acciones sin fijar. Consulta Settings → Actions → General en el nivel de organización. El pinning por sí solo no es suficiente; considera también un período de espera de 7 a 14 días antes de adoptar nuevas versiones de acciones, dado que la mayoría de los compromisos en la cadena de suministro se detectan en el transcurso de una semana.
Evalúa la Exposición de los Runners Auto-Hospedados
Los runners auto-hospedados son persistentes por defecto. Un workflow comprometido puede instalar backdoors que sobreviven entre jobs. La pregunta crítica es si algún repositorio público utiliza tus runners auto-hospedados; de ser así, cualquier colaborador puede enviar un PR que ejecute código arbitrario en tu infraestructura.
Revisa tus grupos de runners en Settings → Actions → Runners y confirma que los repositorios públicos estén excluidos. Para cargas de trabajo sensibles, prefiere runners just-in-time (JIT) que se destruyan tras cada job.
Reemplaza las Credenciales de Nube de Larga Duración con OIDC
Si tus workflows se autentican en AWS, Azure o GCP mediante credenciales estáticas almacenadas como secretos, dichas credenciales quedan expuestas a todas las acciones y pasos de ese job. OpenID Connect (OIDC) elimina este problema emitiendo tokens de corta duración y con alcance limitado al job en tiempo de ejecución. Sin secretos que robar ni credenciales que rotar manualmente.
Verificaciones Adicionales que Vale la Pena Realizar
- Manejo de artefactos: Establece
persist-credentials: falseenactions/checkouta menos que los pasos posteriores necesiten explícitamente el token. Esto evita que el token de checkout permanezca disponible en la configuración local de Git para pasos posteriores del workflow. - Protecciones de entorno: Los secretos de despliegue deben residir en GitHub Environments con revisores requeridos, no como secretos de repositorio simples.
- Attestations de artefactos: Para paquetes publicados, las attestations de artefactos de GitHub proporcionan un vínculo verificable entre una build y su workflow de origen.
- OpenSSF Scorecards: La acción Scorecards puede configurarse para ejecutar verificaciones automatizadas de permisos de tokens, acciones fijadas e inyección de scripts, lo que resulta útil para detectar regresiones de forma automática.
Por Dónde Empezar
Ejecuta una búsqueda en .github/workflows/ para estos patrones: bloques permissions ausentes o configurados como write-all, ${{ dentro de pasos run:, triggers pull_request_target y referencias a acciones sin un SHA completo. Estas cuatro verificaciones pondrán de manifiesto los problemas de mayor riesgo en la mayoría de los repositorios sin necesidad de incorporar nuevas herramientas.
Conclusión
Asegurar GitHub Actions no requiere reescribir toda la pipeline; la mayoría de los incidentes reales se remontan a un conjunto reducido de errores recurrentes: permisos de token demasiado amplios, interpolación insegura de entradas no confiables, referencias mutables a acciones y runners expuestos a código no confiable. Completar las verificaciones anteriores te proporciona una línea base sólida y defendible. Complementa esa base con análisis automatizado mediante OpenSSF Scorecards o herramientas similares, y detectarás las regresiones antes de que lleguen a producción.
Preguntas Frecuentes
La búsqueda de código de GitHub admite consultas a nivel de organización. Busca términos como 'pull_request_target', 'permissions: write-all' o 'github.event.pull_request.title' limitando la búsqueda a path:.github/workflows. Para un análisis más profundo, herramientas como Octoscan, zizmor y la acción OpenSSF Scorecards pueden analizar repositorios completos e informar automáticamente sobre permisos de tokens, acciones sin fijar y puntos de inyección.
No, pero hace que las actualizaciones sean explícitas. Tú decides cuándo actualizar el SHA después de revisar las diferencias entre versiones. Herramientas como Dependabot y Renovate pueden abrir pull requests que actualicen los SHAs fijados de forma automática, brindándote la seguridad del pinning sin la carga de mantenimiento. Aplicar un retraso de 7 a 14 días antes de fusionar estas actualizaciones reduce aún más la exposición a versiones recién comprometidas.
Para AWS, Azure, GCP y la mayoría de los proveedores principales, sí. Los tokens OIDC son de corta duración, están limitados a una ejecución específica del workflow y no pueden ser exfiltrados para uso posterior. La configuración requiere establecer una relación de confianza en tu proveedor de nube, pero elimina la carga de rotación y limita el radio de impacto si un workflow se ve comprometido. Los secretos estáticos siguen siendo una alternativa válida únicamente cuando OIDC no es compatible.
El trigger pull_request se ejecuta en el contexto del fork y no tiene acceso a los secretos del repositorio base, lo que lo hace seguro para ejecutar pruebas sobre código no confiable. El trigger pull_request_target se ejecuta en el contexto del repositorio base con acceso a los secretos, y está pensado para tareas como etiquetar PRs. La combinación peligrosa que hay que evitar es usar pull_request_target junto con un checkout del código del fork.
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.