Jeder Benutzer mit Schreibzugriff auf ein Repository kann sämtliche dort konfigurierten Secrets auslesen. Die Credentials, die Ihre Workflows verwenden, sollten daher nur die Rechte besitzen, die der jeweilige Job tatsächlich benötigt. Die Referenz zur sicheren Nutzung von GitHub sagt dasselbe über das GITHUB_TOKEN: Es sollte nur mit den minimal erforderlichen Berechtigungen ausgestattet werden. Die folgenden fünf Aufgaben nehmen jeweils einen Tag in Anspruch.
Die OWASP Top 10 CI/CD Security Risks benennen die Problemfelder, die dieser Plan berührt: unzureichendes Identitäts- und Zugriffsmanagement (CICD-SEC-2), Missbrauch der Abhängigkeitskette (CICD-SEC-3), Poisoned Pipeline Execution (CICD-SEC-4), unzureichende Credential-Hygiene (CICD-SEC-6) und mangelhafte Prüfung der Artefaktintegrität (CICD-SEC-9).
Montag: Die Möglichkeiten des Tokens reduzieren
Gehen Sie zunächst davon aus, dass jeder, der in das Repository pushen kann, auch dessen Secrets lesen kann. Verkleinern Sie anschließend den Bereich, den diese Credentials erreichen.
GitHub empfiehlt, die Standardberechtigung des GITHUB_TOKEN für Repository-Inhalte auf schreibgeschützt zu setzen und sie nur für einzelne Jobs zu erhöhen. In einer Workflow-Datei sieht das so aus:
permissions:
contents: read
jobs:
release:
permissions:
contents: write
Gehen Sie jeden Workflow durch und fragen Sie, welche Jobs tatsächlich etwas schreiben. Vergeben Sie Schreibrechte nur an diese Jobs und belassen Sie alle übrigen bei read.
Dienstag: Die verwendeten Actions prüfen
Erfassen Sie alle Actions, die in Ihren Workflows zum Einsatz kommen, einschließlich derjenigen, die aus von Ihnen gepflegten wiederverwendbaren Workflows aufgerufen werden. Lesen Sie bei jeder Action den Quellcode der Action selbst und des Repositorys, das sie ausführt.
GitHubs Hinweise zum Audit von Secrets sind konkret, worauf zu achten ist. Prüfen Sie, dass Secrets nicht an unbeabsichtigte Hosts gesendet und nicht explizit in die Log-Ausgabe geschrieben werden.
Wenden Sie danach das Least-Privilege-Prinzip auf die Credentials selbst an. Da Schreibzugriff auf das Repository auch Lesezugriff auf dessen Secrets bedeutet, sollte jedes von einem Workflow genutzte Credential nur das können, was dieser Workflow braucht. Ein Deployment-Token, das den gesamten Cloud-Account administrieren kann, besteht diesen Test nicht.
Mittwoch: Script Injection
Nicht vertrauenswürdige Kontextwerte, etwa der Titel eines Pull Requests, werden zum Problem, wenn sie direkt in ein Inline-Skript eingesetzt werden. Die Referenz von GitHub nennt zwei Wege, das zu vermeiden.
Empfohlen wird eine Action, die den Kontextwert als Argument entgegennimmt, da der Wert dann nie zur Erzeugung eines Shell-Skripts verwendet wird. Wenn Sie ein Inline-Skript beibehalten müssen, setzen Sie den Ausdruck als zwischengeschaltete Umgebungsvariable:
- name: Check PR title
env:
TITLE: ${{ github.event.pull_request.title }}
run: |
if [[ "$TITLE" =~ ^octocat ]]; then exit 0; else exit 1; fi
In GitHubs Beispiel schlägt die Prüfung bei einem Titel wie a"; ls $GITHUB_WORKSPACE" fehl, und der Befehl wird nicht ausgeführt. Der Wert wird im Speicher abgelegt und als Variable verwendet, sodass er nicht in die Skripterzeugung eingreift. GitHub empfiehlt außerdem, Shell-Variablen in doppelte Anführungszeichen zu setzen, um Word Splitting zu vermeiden. Das ist allgemein übliche Shell-Praxis.
Um Kandidaten zu finden, suchen Sie in Ihren Workflows nach Kontextausdrücken und lesen Sie jeden Treffer im zugehörigen run:-Block:
grep -rn 'github.event' .github/workflows
Donnerstag: Secrets und geschützte Environments
Legen Sie Deployment-Secrets in Environments ab und verlangen Sie Reviewer. Ein Workflow-Job kann erst auf Environment-Secrets zugreifen, nachdem ein Reviewer zugestimmt hat.
GitHub stellt klar, dass die automatische Maskierung nicht garantiert ist, weil sich ein Secret-Wert auf vielfältige Weise transformieren lässt. Die Maskierung stützt sich weitgehend auf das Finden einer exakten Übereinstimmung und wird vom Runner durchgeführt. Ein Secret wird also nur dann maskiert, wenn ein Job es verwendet hat und der Runner darauf zugreifen konnte. Nutzen Sie diese Tabelle als Prüfliste:
| Prüfpunkt | Zu beantwortende Frage |
|---|---|
| Registrierte Secrets | Wird jedes noch benötigt, und wann wurde es zuletzt rotiert? |
| Strukturierte Secrets | Ist ein Secret ein JSON-, XML- oder YAML-Block? Teilen Sie es in ein Secret pro Wert auf. |
| Abgeleitete Werte | Ist jeder Base64-kodierte, URL-kodierte oder generierte Wert (zum Beispiel ein signiertes JWT) als Secret registriert? |
| Sonstige sensible Daten | Werden Daten, die kein GitHub-Secret sind, mit ::add-mask::VALUE maskiert? |
| Run-Logs | Tauchen nach Tests mit gültigen und ungültigen Eingaben Secrets in der Ausgabe oder in Fehlerlogs auf? |
| Environments | Liegen Produktions-Secrets hinter verpflichtenden Reviewern? |
Fehlermeldungen können auf unvorhergesehene Weise in STDOUT und STDERR gelangen. Lesen Sie die Logs deshalb nach den Tests von Hand. Gelangt ein nicht maskiertes Secret in ein Run-Log, löschen Sie das Log und rotieren Sie das Secret. Entfernen Sie Secrets, die nicht mehr gebraucht werden, und rotieren Sie die übrigen regelmäßig. Das verkürzt die Zeit, in der ein kompromittierter Wert gültig bleibt.
Freitag: Zuständigkeit für Workflows und Ergebnisse
Dieser Abschnitt ist unsere eigene Empfehlung. Die oben genannten Quellen schreiben sie nicht vor, und sie ist weder GitHub, OWASP noch NIST zuzuschreiben.
Legen Sie fest, wer die Workflows ändern darf, die Ihre Software bauen und veröffentlichen, und dokumentieren Sie das. Wenn Sie auf Ihrer Plattform CODEOWNERS einsetzen, ist das Verzeichnis .github/workflows ein sinnvoller Pfad, den Sie abdecken sollten. Ermitteln Sie anschließend, welcher Workflow Ihre Release-Artefakte erzeugt, denn diesen Artefakten vertrauen Kunden oder Produktionssysteme.
Bei Artefakten tragen die Quellen nur ein Stück weit. OWASP führt mangelhafte Prüfung der Artefaktintegrität als CICD-SEC-9 und unzureichendes Logging und mangelnde Sichtbarkeit als CICD-SEC-10. Das Secure Software Development Framework (SP 800-218) des NIST liefert ein gemeinsames Vokabular, mit dem Käufer und Lieferanten über sichere Entwicklung sprechen können. Keine dieser Quellen gibt ein Rezept für das Signieren vor, daher lassen wir es hier weg.
Legen Sie schließlich fest, wer bemerkt, wenn sich Workflow-Dateien, Secrets oder Environment-Freigaben ändern, und wie diese Person davon erfährt.
Wo Sie beginnen sollten
- Setzen Sie die Standardberechtigung des
GITHUB_TOKENfür Repository-Inhalte auf schreibgeschützt und ergänzen Siepermissions-Blöcke in den Jobs, die mehr benötigen. - Erfassen Sie alle verwendeten Actions, lesen Sie deren Quellcode und prüfen Sie, dass sie keine Secrets an unerwartete Hosts senden oder in Logs ausgeben.
- Suchen Sie nach Kontextausdrücken in
run:-Blöcken und verlagern Sie diese in eine zwischengeschaltete Umgebungsvariable oder in eine Action. - Verschieben Sie Produktions-Secrets in ein Environment mit verpflichtenden Reviewern, teilen Sie strukturierte Secrets in Einzelwerte auf, registrieren Sie abgeleitete Werte und rotieren Sie alles, was in einem Log aufgetaucht ist.
- Dokumentieren Sie, wer die Workflows ändern darf, die Release-Artefakte bauen, und wer bei Änderungen informiert wird.
Wenn Sie eine zweite Meinung zu Ihren Pipelines wünschen: Unsere Leistungen zur sicheren Softwareentwicklung umfassen auch die CI/CD-Konfiguration. Ein Penetrationstest kann zeigen, ob ein Pull Request Ihre Secrets erreichen kann.
