Preventing GitHub Actions Workflow Injection Attacks: Detection & Mitigation Guide
Learn how to detect and prevent GitHub Actions workflow injection vulnerabilities using CodeQL scanning, least privilege principles, and secure coding practices.
Preventing GitHub Actions Workflow Injection Attacks
GitHub Actions workflow injections rank among the most prevalent security vulnerabilities in GitHub repositories, according to GitHub’s 2024 Octoverse report. These attacks exploit untrusted input in workflows, allowing threat actors to execute malicious commands with workflow-level permissions. Security professionals can mitigate these risks using GitHub’s built-in tools, secure coding practices, and automated scanning.
Technical Details of Workflow Injections
GitHub Actions workflow injections occur when an attacker submits malicious input—such as an issue title or branch name—that is subsequently executed by a workflow. The vulnerability stems from the ${{}} syntax, which expands untrusted input during preprocessing, potentially altering workflow behavior. For example:
- name: print title
run: echo "${{ github.event.issue.title }}"
An attacker could craft an issue title like `touch pwned.txt`, which would execute as a command when the workflow runs. Since workflows often inherit elevated permissions, this can lead to unauthorized access or data exfiltration.
Key Attack Vectors
- Untrusted Input Expansion: Direct use of
${{}}inruncommands. - Privilege Escalation: Workflows running with excessive permissions (e.g.,
writeaccess or secret exposure). pull_request_targetTrigger: Bypasses default security restrictions, granting forked PRs access to secrets and write permissions.
Impact Analysis
Successful workflow injections can result in:
- Code Execution: Malicious commands run with workflow permissions.
- Data Breaches: Exposure of repository secrets or sensitive files.
- Repository Compromise: Unauthorized modifications to code or CI/CD pipelines.
The risk extends beyond main branches—attackers can target any publicly visible branch with vulnerable workflows.
Mitigation Strategies
1. Environment Variables for Untrusted Input
Avoid direct expansion of untrusted input in run commands. Instead, pass data via environment variables:
- name: print title
env:
TITLE: ${{ github.event.issue.title }}
run: echo "$TITLE"
While this doesn’t sanitize input, it limits attack surface by preventing command injection.
2. Principle of Least Privilege
Restrict workflow permissions using the permissions key for the GITHUB_TOKEN:
permissions:
contents: read
issues: write
Default to read-only access unless write permissions are explicitly required.
3. Avoid pull_request_target
Use pull_request instead of pull_request_target to block forked PRs from accessing secrets or write permissions. If pull_request_target is unavoidable, audit workflows for injection risks and restrict permissions.
4. CodeQL Scanning for Workflows
GitHub’s CodeQL now supports automated scanning of GitHub Actions workflows. Enable it via:
- Default Setup: Automatically scans protected branches.
- Advanced Setup: Add
actionsto the target languages in your CodeQL configuration.
CodeQL’s taint tracking identifies untrusted data flows, uncovering subtle injection risks.
Recommendations
- Scan All Branches: Vulnerabilities in feature branches can be exploited even if
mainis secure. - Combine Tools with Manual Review: No tool is 100% effective—maintain a security-first mindset.
- Educate Teams: Share GitHub’s four-part security series on workflow hardening.
Next Steps
Enable CodeQL code scanning in your repositories to detect workflow injections proactively. For advanced configurations, include actions as a target language in your CodeQL setup.
For more details, refer to GitHub’s workflow security documentation.