Cualquier usuario con acceso de escritura a un repositorio puede leer todos los secretos configurados en él, por lo que las credenciales que usan sus workflows deben tener el mínimo privilegio que permita el job. La referencia de uso seguro de GitHub dice lo mismo sobre el GITHUB_TOKEN: concédale los permisos mínimos necesarios. Las cinco tareas siguientes requieren un día cada una.
El Top 10 CI/CD Security Risks de OWASP nombra las áreas de riesgo que toca este plan: gestión de identidades y accesos inadecuada (CICD-SEC-2), abuso de la cadena de dependencias (CICD-SEC-3), ejecución de pipelines envenenados (CICD-SEC-4), higiene insuficiente de credenciales (CICD-SEC-6) y validación incorrecta de la integridad de los artefactos (CICD-SEC-9).
Lunes: reduzca lo que puede hacer el token
Parta de la premisa de que cualquiera que pueda hacer push al repositorio puede leer sus secretos. Después, reduzca a qué pueden acceder esas credenciales.
GitHub recomienda establecer el permiso por defecto del GITHUB_TOKEN en solo lectura para el contenido del repositorio y ampliarlo después job por job. En un archivo de workflow:
permissions:
contents: read
jobs:
release:
permissions:
contents: write
Revise cada workflow y pregúntese qué jobs escriben realmente algo. Dé acceso de escritura a esos jobs y deje el resto en solo lectura.
Martes: audite las actions que ejecuta
Haga una lista de todas las actions que se usan en sus workflows, incluidas las que se invocan desde workflows reutilizables que usted mantiene. De cada una, lea el código fuente de la action y del repositorio que la ejecuta.
La guía de GitHub sobre la auditoría de secretos es concreta sobre qué buscar. Compruebe que los secretos no se envían a hosts no previstos y que no se imprimen explícitamente en la salida de los logs.
A continuación, aplique el mínimo privilegio a las propias credenciales. Dado que el acceso de escritura al repositorio implica acceso de lectura a sus secretos, cada credencial que use un workflow solo debería poder hacer lo que ese workflow necesita. Un token de despliegue capaz de administrar toda la cuenta cloud no supera esa prueba.
Miércoles: inyección de scripts
Los valores de contexto no confiables, como el título de una pull request, se convierten en un problema cuando se insertan directamente en un script en línea. La referencia de GitHub ofrece dos formas de evitarlo.
El enfoque recomendado es usar una action que reciba el valor de contexto como argumento, porque así el valor nunca se emplea para generar un script de shell. Si debe mantener un script en línea, asigne la expresión a una variable de entorno intermedia:
- name: Check PR title
env:
TITLE: ${{ github.event.pull_request.title }}
run: |
if [[ "$TITLE" =~ ^octocat ]]; then exit 0; else exit 1; fi
En el ejemplo de GitHub, un título como a"; ls $GITHUB_WORKSPACE" no supera la comprobación y el comando no se ejecuta. El valor se almacena en memoria y se usa como variable, de modo que no interactúa con la generación del script. GitHub también sugiere entrecomillar con comillas dobles las variables de shell para evitar la división de palabras, lo cual es una práctica general de shell.
Para localizar candidatos, busque expresiones de contexto en sus workflows y lea cada coincidencia dentro de su bloque run::
grep -rn 'github.event' .github/workflows
Jueves: secretos y entornos protegidos
Coloque los secretos de despliegue en entornos (environments) y exija revisores. Un job de workflow no puede acceder a los secretos de un entorno hasta que un revisor lo aprueba.
GitHub es claro en que el enmascaramiento automático no está garantizado, porque un valor secreto puede transformarse de varias maneras. El enmascaramiento se basa en gran medida en encontrar una coincidencia exacta y lo realiza el runner, de modo que un secreto solo se enmascara si un job lo usó y el runner pudo acceder a él. Utilice esta tabla como lista de revisión:
| Elemento a revisar | Pregunta a responder |
|---|---|
| Secretos registrados | ¿Sigue siendo necesario cada uno y cuándo se rotó por última vez? |
| Secretos estructurados | ¿Algún secreto es un blob JSON, XML o YAML? Divídalo en un secreto por valor. |
| Valores derivados | ¿Está registrado como secreto cada valor codificado en Base64 o URL, o generado (un JWT firmado, por ejemplo)? |
| Otros datos sensibles | ¿Se enmascaran con ::add-mask::VALUE los datos que no son un secreto de GitHub? |
| Logs de ejecución | Tras probar entradas válidas e inválidas, ¿aparecen secretos en la salida o en los logs de error? |
| Entornos | ¿Están los secretos de producción protegidos por revisores obligatorios? |
Los errores pueden llegar a STDOUT y STDERR de formas que no había previsto, así que revise los logs a mano después de las pruebas. Si un secreto sin enmascarar llega a un log de ejecución, elimine el log y rote el secreto. Elimine los secretos que ya no sean necesarios y rote periódicamente el resto, lo que acorta el tiempo durante el cual un valor comprometido sigue siendo válido.
Viernes: propiedad de los workflows y de su salida
Esta sección recoge un consejo propio. Las fuentes anteriores no lo prescriben y no se atribuye a GitHub, OWASP ni NIST.
Decida quién puede modificar los workflows que compilan y publican su software, y déjelo por escrito. Si utiliza CODEOWNERS en su plataforma, el directorio .github/workflows es una ruta razonable que cubrir. Después, identifique qué workflow produce sus artefactos de release, ya que son los que sus clientes o sus sistemas de producción darán por buenos.
Las fuentes solo llegan hasta cierto punto en materia de artefactos. OWASP recoge la validación incorrecta de la integridad de los artefactos como CICD-SEC-9 y el registro y la visibilidad insuficientes como CICD-SEC-10. El Secure Software Development Framework (SP 800-218) de NIST proporciona un vocabulario común que compradores y proveedores pueden usar para hablar de desarrollo seguro. Ninguna de estas fuentes ofrece una receta de firma, así que no incluimos ninguna.
Por último, decida quién se entera cuando cambian los archivos de workflow, los secretos o las aprobaciones de los entornos, y cómo se entera.
Por dónde empezar
- Establezca el permiso por defecto del
GITHUB_TOKENen solo lectura para el contenido del repositorio y añada bloquespermissionsa los jobs que necesiten más. - Haga una lista de todas las actions en uso, lea su código fuente y compruebe que no envían secretos a hosts inesperados ni los imprimen en los logs.
- Busque expresiones de contexto dentro de bloques
run:y trasládelas a una variable de entorno intermedia o a una action. - Mueva los secretos de producción a un entorno con revisores obligatorios, divida los secretos estructurados en valores individuales, registre los valores derivados y rote todo lo que haya aparecido en un log.
- Deje por escrito quién puede modificar los workflows que generan los artefactos de release y a quién se avisa cuando cambian.
Si desea una segunda opinión sobre sus pipelines, nuestro trabajo de desarrollo de software seguro abarca la configuración de CI/CD. Un test de intrusión puede mostrar si una pull request es capaz de llegar a sus secretos.
