Solución a los secretos vacíos en las solicitudes de extracción de forks en GitHub Actions
GitHub Actions oculta los secretos de las solicitudes de extracción de forks por diseño. Aprende patrones seguros como workflow_run para gestionar credenciales sin comprometer la seguridad.
Traducido automáticamente del original en inglés.
Cuando una canalización de integración continua funciona correctamente en ramas internas pero falla inmediatamente al recibir una contribución de un fork externo, la causa raíz no suele ser un error de configuración. GitHub Actions bloquea intencionadamente el acceso a los secretos del repositorio durante estos eventos específicos para evitar el robo de credenciales. Comprender este límite de seguridad es esencial para mantener canalizaciones robustas en entornos de código abierto o colaborativos sin exponer datos sensibles a código no confiable.
Qué ocurrió
El problema principal surge cuando un trabajo que funciona correctamente en ramas estándar encuentra credenciales vacías durante una solicitud de extracción (pull request) desencadenada por un repositorio bifurcado (fork). Este comportamiento no es un bug ni una mala configuración, sino una característica de seguridad deliberada. GitHub Actions limita el token predeterminado a permisos de solo lectura y oculta completamente los secretos definidos por el usuario en estas ejecuciones. El sistema no ofrece una opción para anular esta restricción porque hacerlo crearía una vulnerabilidad crítica.
La falla se manifiesta de forma sutil. En lugar de un mensaje de error explícito que indique que faltan secretos, la variable de secreto se interpola como una cadena vacía. Las herramientas posteriores fallan entonces con sus propios códigos de error genéricos, como HTTP 401 Unauthorized o quejas sobre entradas requeridas ausentes. Esta ambigüedad suele llevar a los ingenieros a perder tiempo depurando la conectividad de red o las versiones de las herramientas, en lugar de reconocer el modelo de permisos en juego. Un simple paso de diagnóstico puede confirmar el problema verificando si la fuente del evento es un fork y si el valor del secreto no está vacío, proporcionando claridad inmediata.
Este diseño existe porque el código que se ejecuta en una solicitud de extracción de un fork es controlado por el colaborador, no por el propietario del repositorio. Si los secretos estuvieran disponibles, un actor malicioso podría modificar un script de compilación o un archivo de prueba para exfiltrar claves de despliegue y tokens de API en un solo commit. Al establecer el límite de confianza en el punto de ejecución del código, GitHub garantiza que el código no confiable nunca obtenga acceso a variables de entorno sensibles. Los únicos disparadores que llevan secretos completos son aquellos que se ejecutan en el contexto del repositorio base, como ciertos eventos objetivo o ejecuciones de workflows subsiguientes.
Detalles clave
- GitHub Actions oculta todos los secretos del repositorio en los eventos
pull_requestoriginados desde forks. - El
GITHUB_TOKENse limita automáticamente a permisos de solo lectura para estos eventos. - Las fallas aparecen como errores genéricos de autenticación o advertencias de entrada ausente, en lugar de problemas explícitos de secretos.
- El disparador
pull_request_targetproporciona secretos pero hace checkout del código base por defecto, requiriendo un manejo cuidadoso para evitar ejecutar código no confiable de la PR. - Las solicitudes de extracción de Dependabot tampoco tienen acceso a los secretos estándar del repositorio, lo que requiere una configuración separada en el almacén de secretos de Dependabot.
- El uso de
workflow_runpermite que un segundo workflow confiable acceda a los secretos después de que una compilación inicial no confiable haya finalizado.
Contexto
Para entender por qué existe esta restricción, hay que reconocer la diferencia entre contextos de código confiable y no confiable. En un push estándar a una rama, el código pertenece a los colaboradores del repositorio que ya tienen acceso a los recursos del proyecto. En una solicitud de extracción de un fork, el código proviene de un usuario externo que puede no tener permisos de escritura. Si el sistema de CI ejecutara este código externo con privilegios administrativos completos, estaría otorgando efectivamente a cada colaborador la capacidad de robar credenciales.
La solución implica separar el proceso de compilación de las fases de despliegue o pruebas de integración. La fase inicial ejecuta el código no confiable en un entorno aislado (sandbox) sin secretos. Una vez que esta fase produce artefactos, como binarios compilados o informes de pruebas, una segunda fase puede consumir estos artefactos en un contexto confiable. Esta separación asegura que, aunque el código sea probado, nunca interactúe directamente con credenciales de infraestructura sensibles a menos que esté explícitamente aprobado y aislado.
Por qué importa
Para equipos que gestionan proyectos de código abierto o aceptan contribuciones externas, ignorar este modelo de seguridad puede conducir a brechas graves. Intentar anular la restricción utilizando disparadores inseguros como pull_request_target con un checkout de la cabeza (head) de la solicitud de extracción crea un escenario de "pwn request". En esta situación, el runner de CI ejecuta código arbitrario del colaborador con acceso completo a todos los secretos del repositorio. Este riesgo es particularmente alto en repositorios públicos populares donde los atacantes escanean activamente buscando tales malas configuraciones para cosechar credenciales de nube.
Por el contrario, no manejar esto correctamente resulta en experiencias rotas para los colaboradores. Cuando desarrolladores externos envían solicitudes de extracción que fallan debido a credenciales ausentes, pueden asumir que el proyecto está mal mantenido o roto. Esta fricción desalienta la participación comunitaria y aumenta la carga de mantenimiento para los miembros del equipo central, quienes deben explicar manualmente la falla. Implementar lógica clara de omisión (skip logic) o workflows confiables separados asegura que los colaboradores reciban retroalimentación precisa mientras se mantiene seguro el entorno de producción.
Además, muchos equipos pasan por alto que algunas pruebas de integración no requieren realmente secretos en vivo. Reemplazando llamadas a servicios externos con stubs locales o contenedores de servicio, los equipos pueden ejecutar pruebas más completas dentro del contexto de fork no confiable. Este enfoque reduce la necesidad de configuraciones complejas de múltiples workflows y acelera los bucles de retroalimentación para los colaboradores. Cambia el foco de gestionar el acceso a secretos a mejorar el aislamiento y la fiabilidad de las pruebas.
Qué puedes hacer
- Audita tus archivos de workflow para identificar pasos que genuinamente requieren secretos versus aquellos que pueden usar stubs locales o contenedores de servicio.
- Implementa una comprobación condicional en tus trabajos para omitir pasos dependientes de secretos cuando se ejecuten en solicitudes de extracción de forks, usando un aviso visible para explicar la omisión.
- Usa el evento
workflow_runpara desencadenar un segundo workflow que descargue artefactos de la compilación inicial y realice tareas dependientes de secretos en un contexto confiable. - Si usas
pull_request_target, asegúrate de nunca hacer checkout del SHA head de la solicitud de extracción; quédate siempre con el código de la rama base para prevenir inyección de código. - Configura secretos separados para Dependabot, ya que no tiene acceso a los secretos estándar del repositorio.
- Publica comentarios en las solicitudes de extracción explicando claramente que ciertas pruebas requieren secretos y serán ejecutadas por un maintainer o mediante un workflow secundario, para evitar confusiones.



