flawopen.com/Seguridad de GitHub Actions/Inyección de scripts en workflows
¿Cómo ocurre la inyección de scripts en GitHub Actions y cómo se evita?
Un workflow puede convertir datos en sintaxis shell si inserta un título de issue o un campo de pull request en el script antes de iniciar Bash. Es como una impresora de credenciales que confunde un nombre con instrucciones. Pasa el valor mediante una variable de entorno y entrecomilla su expansión al usarlo como dato; así Bash lo recibe separado del script. El impacto depende del token, los secretos, el runner y el evento.
${{ ... }} antes de iniciar el shell. En un bloque run: puede insertar la entrada en el código fuente del script.env: lo pasa como dato del proceso. Entrecomilla la expansión del shell y evita eval o generar otro script.Causa raíz: los datos se insertan antes de analizar el shell
GitHub expande expresiones antes de iniciar el shell de un paso run. Si un título no confiable se interpola directamente en el script, sus caracteres pueden modificar el programa shell en vez de seguir siendo datos. Un valor pasado mediante env: permanece separado del texto del script; entrecomillarlo lo conserva como argumento en comandos normales. Esto no hace seguro usarlo después con eval, otro intérprete o una herramienta que trate los argumentos como código. GitHub identifica los campos de eventos no confiables como entrada controlable por atacantes.
# INSEGURO: los datos del evento forman parte del script shell generado
name: Issue Triage
on:
issues:
types: [opened]
permissions:
contents: read
jobs:
triage:
runs-on: ubuntu-latest
steps:
- name: Print issue title
run: |
printf 'Title: %s\n' "${{ github.event.issue.title }}"
# Más seguro: pasa el valor del evento mediante el entorno
name: Issue Triage
on:
issues:
types: [opened]
permissions:
contents: read
jobs:
triage:
runs-on: ubuntu-latest
steps:
- name: Print issue title
env:
ISSUE_TITLE: ${{ github.event.issue.title }}
run: |
printf 'Title: %s\n' "$ISSUE_TITLE"
Ruta de ataque resumida
Un usuario controla un campo del evento
Por ejemplo, un título de issue público, un pull request o el nombre de una rama se convierte en entrada del workflow.
El workflow lo inserta en el código shell
Una expresión directa ${{ github.event... }} en run: se expande antes de que Bash analice el script.
El shell interpreta el programa alterado
Si el valor cambia la sintaxis, pueden ejecutarse comandos con la identidad del proceso del job. Si sigue siendo dato y se usa como argumento entrecomillado, no se produce esa inyección.
Los permisos del job determinan el impacto
Un job comprometido solo alcanza los recursos expuestos a esa ejecución: permisos efectivos de GITHUB_TOKEN, secretos disponibles y recursos del runner. Revisa con cuidado los eventos de issues y pull_request_target.
Lista de endurecimiento
- ✓Nunca interpoles valores no confiables de
github.eventdirectamente en un scriptrun:. Pásalos porenv:y usa expansiones entrecomilladas, como"$ISSUE_TITLE". - ✓Mantén esos valores fuera de
eval, scripts generados, cadenas de comandos shell y herramientas que interpreten argumentos como código u opciones. - ✓Define
permissions:restrictivas para el workflow o el job y concede escritura solo al job que la necesite. - ✓Trata
pull_request_targetyworkflow_runcomo límites privilegiados. No descargues, compiles ni ejecutes código o artefactos no confiables en ese contexto. - ✓No escribas datos multilínea no confiables en
GITHUB_ENVniGITHUB_PATH. GitHub impide sobrescribirGITHUB_*yRUNNER_*, pero otros valores pueden afectar pasos posteriores. - ✓Fija las actions de terceros a SHA completos revisados, usa runners aislados y efímeros para jobs no confiables y habilita CodeQL para workflows de Actions.
Fuentes primarias
- GitHub Docs — inyección de scripts
- GitHub Docs — referencia de uso seguro
- GitHub Docs — uso seguro de pull_request_target
- GitHub Docs — comandos de workflow y GITHUB_ENV
- GitHub Docs — permisos de GITHUB_TOKEN