Intro
Sometimes secrets in the real world aren't as secret as we think! Although it can be scary, it’s really easy to show the content of your secret in GitHub actions log. You might not mind if your repo is private but if it is public and your Open Source project still needs things to stay private like secrets, It’s a real liability.
In this blog post, we’ll reveal how seemingly hidden GitHub secrets can sometimes slip out in plain sight. All it takes is a tiny code snippet in my workflow. But first, let’s review some fundamentals about GitHub secrets.
GitHub Actions and Secrets
Secrets in public repositories are automatically masked in build logs & show as strings of "*" characters.
There are organization, repository and environment level secrets (contexts).
In case of conflict, organization is overridden by repository and repository is overridden by env values.
secrets.GITHUB_TOKEN is a temporary token for each workflow run.
Beware: GitHub Actions logs of your public repo are visible to anyone as opposed to private ones.
Let’s crack your secret
What if the secret is already visible in your workflow log of your public repo?
In order to run this test and show your secret in your build log. You can either
Clone my repo then load it back to your own GitHub.
Just reuse the code in my Workflow.
PREREQUISITES
A repository + branch (optional)
Example Repo: brokedba/githubactions_hacks Branch: git_actions
An environment
Name: lab_tests , With deployment branch set to `selected branch`: i.e git_actions
A Secret
secret name: TEST_SECRET secret value: MYPassword
A workflow
You can download test_secret.yml located under .github/workflows as below display might not be properly indented.
# example: “test secret” action that shows the spaced content of your secret
name: 'My_secret_test_Workflow'
on:
push:
branches: [ "git_actions" ]
paths:
- '.github/workflows/test_secret.yml'
jobs:
job_test_secrets:
runs-on: ubuntu-latest
environment: test-labs
#Use default shell of the GitHub Actions runner defaults: run:
shell: bash
steps:
# Checkout the repository to the GitHub Actions runner
- name: Checkout
uses: actions/checkout@v3
# Show how to print unmasked GitHub secrets to the console
- name: Step 1 - Echo out a GitHub Actions Secret to the logs
run: |
# ==> show masked value ******
echo ${{ secrets.TEST_SECRET }}
echo "Trick to echo GitHub Actions Secret: "
# ===> show the real content with spaces 'M Y P a s s w o r d'
echo ${{secrets.TEST_SECRET}} | sed 's/./& /g'
-
The content has nothing
more but a check out action and a tiny code snippet that consumes
the secret.
-
We chose the push as trigger
event and the git_actions as target branch
-
We set paths so
anytime the yaml workflow is changed a pipeline is
triggered
-
Finally a simple
sed expression piped into the “echo secret”
command
Workflow Execution result
A little change commit for test_secret.yml and Voila!
My secret is now readable in plain sight on the build log
You are never safe
Now, this is just a small example of how your secret can be revealed in the logs and although you’d argue that no one is going to add this code in their workflow in purpose but there are many ways to infiltrate your Github pipelines and inject malicious code from an external PR to either extract secrets or damage your deployments or artifacts.
It could be due to misconfiguration of your workflows or by using untrusted actions from Marketplace.
What we already know
More Vulnerabilities
Exploitability and impact of untrusted input such as supply-chain compromise is real, Here are few examples
Any maintainer can update a branch or a tag, hence a corrupted Public actions@v1 can have an updated tag that has a malicious code inside & everyone will call the wrong release. Read more Here
There a list of event context data that might be attacker controlled like issue, title
, body
, & comment
The attacker server can use the GitHub API to modify repository content, including releases.
The attacker can exfiltrate repository and organizational secrets other than the ones of the workflow by creating a new workflow via personal access token (PAT).
By default, pull requests from first-time contributors require approval from a maintainer but following PR might be malicious
pull_request_target trigger event: will allow any PR submitted from a forked repo to run in the context of your main repository getting access to secrets and write access to the original repo
Detailed studies on GitHub actions Vulnerabilities
Best practices
Again if your workflow is in a public
repo, remember below safety measures ⏬
Use commit hash(@sha1) as version tag when calling third-party actions to shield yourself.
You must only invite trustworthy outside collaborators.
Don't have any workflow with a pull_request_target
trigger.
Avoid using untrusted public actions and if you have to always test them in dummy repos first.
Prefer OpenID Connect to access cloud resources
GITHUB_TOKEN and PAT should always be granted the minimum required permissions
Don’t reference directly values that you don’t control like
Avoid run
steps (inline script) and use external actions instead
Sanitize your input using environment variables
scan for secrets in the source code itself using ggshield-action
If you create a credential from a secret (e.g. base64 an API key), you should mask the new value so it doesn't leak.
Have the "Require approval for all outside collaborators" option for GitHub Actions turned on
Below is an excellent cheat-sheet on available security best practices
Conclusion:
This blog post aimed to shed light on potential security pitfalls in GitHub Actions.
Even masked secrets can be uncovered with some clever tricks as shown in my workflow code snippet .
There are even more vulnerabilities out there as mentioned in the second part of this article
Again if your workflow is in a public repo, remember to apply the mentioned safety measures
You should always be careful about how you set up your workflows
Next, I will write a post about cleaning up your GitHub action cache after use.
Stay tuned
No comments:
Post a Comment