- Los secretos de GitHub Actions son variables de entorno cifradas y con alcance que deben definirse cuidadosamente en los niveles de repositorio, entorno y organización.
- La seguridad se basa en el mínimo privilegio, evitar la exposición de registros, rotar y auditar secretos y aislar entornos de producción sensibles.
- Los riesgos de inyección de scripts, acciones de terceros y ejecutores autohospedados requieren fijación, revisión de código y políticas estrictas de ejecución y permisos.
- OpenID Connect y los administradores de secretos externos ayudan a reemplazar las credenciales de larga duración con tokens de corta duración y flujos de trabajo secretos centralizados y auditables.

Administrar secretos en GitHub Actions es uno de esos temas que parece simple a primera vista, pero rápidamente se convierte en un problema de seguridad crítico una vez que sus canales comienzan a tocar producción, proveedores de nube y servicios de terceros. Sus flujos de trabajo de CI/CD tratan rutinariamente con claves API, contraseñas de bases de datos, claves SSH, tokens y más, y cada uno de esos valores es un punto de entrada potencial para un atacante si se maneja sin cuidado.
En esta guía, analizaremos en profundidad cómo funcionan los secretos en GitHub Actions, cómo configurarlos a nivel de repositorio, entorno y organización, cómo reforzar los flujos de trabajo contra fugas y ataques a la cadena de suministro, y cuándo tiene sentido incorporar administradores de secretos externos. La idea es brindarle una descripción general práctica y centrada en la seguridad para que pueda mantener sus pipelines rápidos. y seguro sin convertir el trabajo diario en un dolor de cabeza.
¿Qué son exactamente los secretos de GitHub Actions?
En GitHub Actions, un “secreto” es una variable de entorno cifrada cuyo valor está oculto en la interfaz de usuario, los registros y el contenido del repositorio. Lo define una vez (a nivel de repositorio, organización o entorno) y luego hace referencia a él en su flujo de trabajo YAML usando el secrets. contexto, por lo que sus pipelines pueden usar valores sensibles sin tener que comprometerlos nunca con el código base.
Bajo el capó, GitHub cifra los secretos usando criptografía fuerte (cajas selladas de Libsodium) incluso antes de que lleguen a los servidores de GitHub, y los valores solo se descifran en el tiempo de ejecución en el ejecutor del flujo de trabajo. Una vez creados, los secretos son inmutables desde la interfaz de usuario: puedes sobrescribirlos, pero no puedes volver a leerlos, y cuando aparecen en los registros se enmascaran automáticamente con *** donde sea posible.
Este modelo viene con algunas restricciones de diseño importantes que debe tener en cuenta: los secretos no se pueden recuperar a través de la interfaz de usuario o la API, se eliminan de los registros y residen en un ámbito específico: Repositorio, entorno dentro de un repositorio u organización. Elegir el alcance correcto es la primera gran decisión para una estrategia de secretos sensata.

Secretos del repositorio, entorno y organización
GitHub ofrece tres capas principales para secretos: secretos de repositorio, secretos de entorno y secretos de organización, cada uno con sus propios casos de uso y reglas de precedencia. Comprender cómo interactúan le ayudará a evitar conflictos y a mantener los valores sensibles donde pertenecen.
Secretos a nivel de repositorio
Los secretos del repositorio están vinculados a un único repositorio y están disponibles para todos los flujos de trabajo en ese repositorio. Son perfectos para valores específicos del proyecto, como la clave API de un servicio, una contraseña de implementación o un token de webhook utilizado solo por ese repositorio.
Para crear un secreto de repositorio desde la interfaz de usuario, vaya al repositorio de destino, abra “Configuración” → “Secretos y variables” → “Acciones” y luego haga clic en “Nuevo secreto de repositorio”. Le asignas un nombre en mayúsculas con guiones bajos (por ejemplo PAYMENTS_API_KEY), pegue el valor secreto y guárdelo; a partir de ese momento, los flujos de trabajo pueden acceder a él como ${{ secrets.PAYMENTS_API_KEY }}.
Cualquier persona con acceso de escritura al repositorio puede hacer referencia a estos secretos en los flujos de trabajo, por lo que los permisos en el repositorio en sí se convierten en parte de su historia de seguridad. Si otorga acceso de escritura de manera casual a muchos usuarios, implícitamente les está otorgando acceso para usar todos los secretos del repositorio en la automatización.
Secretos específicos del entorno
Los secretos de entorno se ubican un nivel por debajo de los secretos del repositorio y le permiten definir diferentes valores por entorno, como dev, staging o production. Están asociados a un entorno designado y pueden protegerse con reglas como revisores obligatorios o temporizadores de espera antes de que un trabajo pueda ejecutarse en ellos.
Puedes configurarlos yendo a “Configuración” → “Entornos”, creando o seleccionando un entorno y luego agregando secretos dentro de esa configuración del entorno. Los nombres secretos todavía se usan secrets. contexto (por ejemplo secrets.PROD_DB_PASSWORD), pero los valores solo están disponibles para los trabajos que se ejecutan explícitamente en ese entorno.
Un detalle clave es que los secretos del entorno anulan los secretos del repositorio cuando comparten el mismo nombre. Eso significa que puedes definir DB_PASSWORD a nivel de repositorio para usos locales/de prueba y luego tener un diferente DB_PASSWORD como un secreto de entorno para producción que tiene prioridad en los trabajos de producción, sin cambiar la sintaxis del flujo de trabajo.
Los entornos también habilitan reglas de protección como “revisores obligatorios” o “solo de ciertas ramas”, lo que resulta increíblemente útil para proteger el acceso a sus secretos más confidenciales. Por ejemplo, un entorno de producción podría requerir la aprobación de DevOps antes de que se pueda ejecutar cualquier trabajo que utilice sus secretos.
Secretos de toda la organización
Los secretos de la organización se comparten entre múltiples repositorios en una organización y son ideales para credenciales que se reutilizan ampliamente, como un webhook de Slack compartido o un token de API de métricas centrales. Reducen la duplicación y facilitan la rotación porque actualizas el secreto una vez y todos los repositorios que lo consumen toman el nuevo valor.
Los administradores los crean en la sección “Configuración” → “Secretos y variables” → “Acciones” de la organización, haciendo clic en “Nuevo secreto de la organización” y luego eligiendo qué repositorios pueden acceder a ese secreto. Puede permitir todos los repositorios actuales y futuros o restringirlos estrictamente a un subconjunto específico.
Hay una cadena de precedencia que debes tener en cuenta: secreto de la organización < secreto del repositorio < secreto del entorno cuando los nombres chocan. En otras palabras, un secreto de entorno gana sobre un secreto de repositorio, que gana sobre un secreto de organización si todos comparten la misma clave.
Cómo se comportan los secretos en tiempo de ejecución
Una vez definidos, los secretos quedan disponibles para los trabajos en tiempo de ejecución a través de secrets contexto y se inyectan como variables de entorno cuando se hace referencia a ellas. No se exportan ampliamente a cada paso de forma predeterminada; los conecta explícitamente en su env: bloques o pasarlos a acciones que admitan secretos como entradas.
GitHub también ofrece una opción especial GITHUB_TOKEN por ejecución de flujo de trabajo, que no es un secreto definido manualmente, pero se comporta como uno y se utiliza a menudo para llamadas de API u operaciones de repositorio. Puede (y debe) ajustar los permisos detallados de este token utilizando el permissions: bloque para que tenga el alcance mínimo necesario para cada trabajo.
Para reducir la exposición accidental, GitHub enmascara cualquier valor que coincida con un secreto registrado en los registros de flujo de trabajo y lo reemplaza con ***. Este enmascaramiento se realiza en el lado del ejecutor y generalmente funciona bien con cadenas sin procesar, pero asume que el valor secreto exacto aparece en la salida. Si se transforma el secreto (por ejemplo, se codifica en base64 o se incrusta en JSON estructurado), la máscara podría no detectarlo.
Dado que el enmascaramiento es una medida de máximo esfuerzo y no está garantizado matemáticamente, debe diseñar flujos de trabajo para evitar imprimir secretos o sus derivados en los registros y usar comandos de enmascaramiento para valores adicionales que genere en tiempo de ejecución. Considere los registros como potencialmente visibles para más personas de las que espera y suponga que todo lo que se imprime podría filtrarse.
Uso práctico: referenciar secretos en flujos de trabajo
La mayoría de las veces, utilizará secretos asignándolos a variables de entorno en un paso o trabajo específico y luego permitirá que sus scripts o herramientas lean desde el entorno. Un patrón clásico se ve así:
– nombre: Implementar en API
entorno:
CLAVE API: ${{ secrets.PROD_API_KEY }}
ejecutar: |
curl -H “Autorización: Portador $API_KEY” https://api.example.com/deploy
También puede escribir un secreto en un archivo en el ejecutor, lo cual es seguro siempre que el archivo permanezca dentro del espacio de trabajo efímero del trabajo y no se confirme ni se cargue como un artefacto. Por ejemplo, almacenar una clave SSH:
– nombre: Escribe la clave SSH en el archivo
shell: bash
entorno:
CLAVE SSH: ${{ secretos.SSH_KEY }}
ejecutar: |
echo “$SSH_KEY” > clave
clave chmod 600
En los registros solo verás el comando de shell real (con $SSH_KEY), pero no el valor secreto en sí, que será redactado u oculto. Debido a que los ejecutores alojados en GitHub se destruyen una vez finalizado el trabajo, ese archivo temporal desaparece con la máquina virtual; en los ejecutores autoalojados debes ser mucho más estricto con la limpieza.
Prácticas recomendadas de seguridad para secretos en GitHub Actions
No basta con utilizar la interfaz de secretos; necesitas un conjunto de hábitos y medidas de seguridad para minimizar el radio de explosión si algo sale mal. GitHub proporciona muchas perillas, pero depende de usted girarlas correctamente.
Aplicar el principio de privilegio mínimo.
Cada secreto y cada token debe otorgar únicamente los permisos absolutamente necesarios para una tarea determinada. Para los servicios externos, cree credenciales dedicadas con permisos limitados (por ejemplo, “solo implementar” o “métricas de solo lectura”) en lugar de reutilizar claves de administrador completo.
El mismo principio se aplica a la función incorporada. GITHUB_TOKEN; establecer los permisos predeterminados al mínimo indispensable (a menudo contents: read) y luego aumentar los permisos sólo en trabajos específicos que necesitan más. Configura esto con un permissions: sección a nivel de flujo de trabajo o de trabajo para que un trabajo comprometido no pueda realizar escrituras arbitrarias de forma silenciosa.
Evite imprimir o codificar secretos en los registros
Los secretos nunca deben codificarse en archivos de flujo de trabajo ni imprimirse en texto sin formato para facilitar la depuración. Si tiene otros valores confidenciales que no están registrados como secretos de GitHub (por ejemplo, un token generado en tiempo de ejecución), aún puede pedirle al ejecutor que los trate como secretos usando la sintaxis del comando:
echo “::add-mask::$GENERATED_TOKEN”
Los blobs estructurados como JSON, XML o documentos YAML grandes son especialmente peligrosos como secretos porque el enmascarador de GitHub depende de la coincidencia exacta de cadenas. Si coloca múltiples valores sensibles dentro de una cadena JSON grande y la usa como un único secreto, pequeños cambios de formato pueden provocar que el enmascaramiento falle; en su lugar, defina secretos individuales para cada campo delicado.
Revise siempre los registros al probar flujos de trabajo, prestando especial atención a los mensajes de error y los seguimientos de la pila. Algunas herramientas repiten felizmente comandos y banderas en stderr, lo que puede incluir accidentalmente valores secretos a menos que evites explícitamente ese patrón.
Rotar y auditar los secretos periódicamente
La rotación de secretos es tediosa, pero no es negociable si te preocupa la seguridad: dejar las credenciales sin cambios durante años aumenta la ventana de oportunidad para los atacantes. Una base razonable es rotar los secretos de producción más críticos mensualmente, los de alto riesgo cada dos meses y todo lo demás al menos trimestralmente.
Puede automatizar parte de esto utilizando la API REST de GitHub para secretos, que le permite obtener la clave pública de un repositorio u organización y cargar nuevos valores cifrados. Esto es particularmente útil para organizaciones grandes con muchos repositorios que comparten cuentas de servicio y necesitan rotarlas rápidamente en respuesta a incidentes.
La auditoría es igualmente importante: revise periódicamente la lista de secretos configurados y elimine aquellos que ya no se utilizan, y utilice los registros de seguridad/auditoría de GitHub para rastrear eventos como org.update_actions_secret. De esta manera, sabes quién cambió qué y cuándo, y puedes correlacionar cambios sospechosos con otras actividades.
Utilice la separación basada en el entorno
Los secretos de entorno son una de las formas más sencillas de fortalecer sus pipelines, ya que le permiten aislar valores altamente sensibles (como credenciales de base de datos de producción) detrás de aprobaciones explícitas. Puede requerir revisores humanos, limitar qué ramas pueden implementarse e incluso agregar temporizadores de enfriamiento antes de que comience una implementación.
Un patrón común es tener una staging entorno con protecciones suaves y una production entorno con reglas más estrictas y secretos separados. Luego, los flujos de trabajo definen trabajos que apuntan a cada entorno, lo que garantiza que los secretos de producción nunca se utilicen accidentalmente en trabajos de prueba.
Elija convenciones de nombres claras
Nombrar bien los secretos le ahorrará conjeturas frustrantes y confusiones peligrosas. En lugar de nombres genéricos como API_KEY, codificar el servicio y el entorno en el nombre, por ejemplo STRIPE_PROD_API_KEY or AWS_STAGING_DEPLOY_ROLE_ARN.
Los equipos que se ocupan de muchos servicios a menudo adoptan un patrón como <SERVICE>_<ENV>_<PURPOSE>. Así que es posible que tengas SLACK_PROD_ALERTS_WEBHOOK, GCP_DEV_BUILD_SERVICE_ACCOUNT y DB_STAGING_PASSWORDEsto hace que sea mucho más obvio qué secreto debe usarse en qué trabajo.
Protección contra la inyección de scripts y riesgos de terceros
Los secretos no solo corren riesgo debido a una configuración incorrecta; también son blancos tentadores para la inyección de scripts en flujos de trabajo y acciones maliciosas o comprometidas de terceros. Un solo paso vulnerable puede filtrar todos los secretos accesibles para el trabajo si no tienes cuidado.
Mitigación de la inyección de scripts en pasos en línea
Cuando interpola datos no confiables (como títulos de solicitudes de extracción, nombres de ramas o comentarios de problemas) directamente en scripts de shell, abre la puerta a la inyección. Por ejemplo, un título de PR podría diseñarse para salir de un comando y ejecutar código de shell arbitrario en su ejecutor.
El enfoque más seguro es manejar lógica compleja en acciones JavaScript/TypeScript propias o bien auditadas y pasar valores no confiables como entradas en lugar de incorporarlos en el shell. En ese modelo, la acción recibe cadenas como argumentos y las procesa sin generar scripts de shell que puedan ser secuestrados.
Si debe utilizar el shell en línea, primero almacene los valores no confiables en las variables de entorno y luego haga referencia a esas variables, preferiblemente entre comillas dobles. De esta manera, el valor se trata como datos en lugar de como parte de la estructura del script, lo que hace que los intentos de inyección tengan muchas menos probabilidades de tener éxito.
Fijar y revisar acciones de terceros
Cada acción de terceros que incorpora a su flujo de trabajo se ejecuta con acceso al entorno y los secretos del trabajo, por lo que debe tratarlas como dependencias de código que requieren escrutinio. Una acción maliciosa o comprometida puede leer secretos y enviarlos a un atacante con una sola llamada HTTP.
La mejor práctica es fijar acciones por SHA de confirmación completa en lugar de solo etiquetas o ramas, porque las etiquetas se pueden mover o sobrescribir. Un SHA se refiere a una versión exacta del código, lo que hace que sea mucho más difícil para un atacante inyectar silenciosamente un nuevo comportamiento sin que usted actualice el flujo de trabajo.
Antes de utilizar una acción, revise su código fuente (o al menos haga una revisión de seguridad) para asegurarse de que maneja los secretos de manera responsable y no los registra ni los envía a puntos finales desconocidos. Si utiliza acciones de mercado, verifique al editor cuando sea posible y confíe en Dependabot para que le avise sobre vulnerabilidades y actualizaciones.
Corredores alojados vs. autoalojados y exposición secreta
El lugar donde se ejecutan sus flujos de trabajo tiene un gran impacto en la seguridad con la que se manejan los secretos. Los ejecutores alojados en GitHub y los ejecutores autoalojados se comportan de manera muy diferente en términos de aislamiento y persistencia.
Los ejecutores alojados en GitHub activan nuevas máquinas virtuales para cada trabajo, ejecutan su flujo de trabajo y luego las desmantelan. Esto le proporciona un entorno limpio en todo momento y garantiza que todos los archivos o variables de entorno (incluidos los secretos escritos en el disco) se destruyan una vez que se complete el trabajo.
Por el contrario, los ejecutores autoalojados son máquinas de larga duración que usted administra, lo que significa que cualquier código con acceso a secretos puede potencialmente persistir o filtrarlos más allá de la vida de un solo trabajo. En los repositorios públicos, los ejecutores autoalojados son particularmente riesgosos porque los colaboradores no confiables pueden abrir solicitudes de extracción que activen flujos de trabajo.
Si utiliza ejecutores autohospedados, aíslelos por nivel de sensibilidad, restrinja qué repositorios pueden usar qué ejecutores y sea paranoico acerca de qué más se encuentra en esas máquinas (claves SSH, credenciales de la nube, acceso de red a servicios internos, etc.). Algunas organizaciones utilizan ejecutores autoalojados “justo a tiempo” (JIT) que se crean a través de API para un solo trabajo y luego se destruyen, pero incluso en ese caso debe asegurarse de que los trabajos no compartan el mismo ejecutor inesperadamente.
Uso de OpenID Connect (OIDC) en lugar de secretos de nube de larga duración
Uno de los mayores logros en materia de higiene secreta en GitHub Actions es reemplazar las claves de acceso a la nube de larga duración por credenciales de corta duración a través de OpenID Connect. En lugar de almacenar claves de AWS, Azure o GCP como secretos, sus flujos de trabajo solicitan tokens temporales del proveedor de la nube utilizando GitHub como proveedor de identidad.
El flujo se ve aproximadamente así: el trabajo solicita un JWT firmado desde el punto final OIDC de GitHub, su proveedor de nube valida ese token y lo intercambia por credenciales de corta duración, y el flujo de trabajo usa esas credenciales mientras dura el trabajo. Ningún secreto estático necesita vivir en GitHub.
Por ejemplo, con AWS configura un rol de IAM que confía en el proveedor OIDC de GitHub y restringe qué repositorios/ramas pueden asumir ese rol. Luego, en tu flujo de trabajo utilizas una acción como aws-actions/configure-aws-credentials con permisos OIDC habilitados para obtener credenciales sobre la marcha.
Este enfoque tiene múltiples beneficios: no hay nada que rotar dentro de GitHub, los tokens tienen una vida útil automáticamente corta, el acceso tiene un alcance limitado y se obtienen registros de auditoría completos en el lado de la nube que rastrean cada asunción de rol. Para entornos de alta seguridad, OIDC debe ser el valor predeterminado donde sea compatible.
Herramientas nativas y administradores de secretos externos
Los secretos integrados de GitHub son excelentes para muchos escenarios, pero en algún momento es posible que desees un administrador de secretos más central y con más funciones que abarque múltiples plataformas y entornos. Herramientas como HashiCorp Vault, Infisical o Doppler se utilizan con frecuencia junto con GitHub Actions para este propósito.
Estos sistemas pueden gestionar secretos dinámicos (por ejemplo, generar usuarios de bases de datos de corta duración), políticas de control de acceso avanzadas, registros de auditoría detallados y flujos de trabajo de rotación que van más allá de lo que ofrece GitHub por sí solo. Luego, las acciones de GitHub se autentican en estos administradores (a menudo a través de OIDC u otro método de autenticación), obtienen los secretos que necesitan en el tiempo de ejecución y los usan sin almacenar nunca credenciales de largo plazo en el repositorio.
También hay acciones comunitarias y complementos diseñados para extraer secretos de administradores externos directamente a los flujos de trabajo. Al usarlos, se aplica el mismo consejo: revisar la fuente de la acción, fijarla a un SHA de confirmación y limitar los privilegios otorgados al token o rol que utiliza para llegar al sistema externo.
La gestión segura de secretos en GitHub Actions significa elegir el alcance correcto para cada secreto, aplicar el mínimo privilegio, usar entornos y OIDC cuando sea adecuado, tratar los registros y las acciones de terceros como posibles superficies de ataque y apoyarse en administradores de secretos externos cuando sus requisitos de escala o cumplimiento lo exijan. Con estas prácticas implementadas, sus canales de CI/CD pueden seguir siendo flexibles y rápidos y, al mismo tiempo, reducir drásticamente las posibilidades de que un token extraviado o un flujo de trabajo mal revisado se conviertan en un incidente en toda regla.