So GitHub is the thing now! Everyone is migrating to GitHub leaving their Azure DevOps or whatever they were using for the benefit of GitHub. And, I understand! GitHub is simple, and also is this new shiny thing (not as an open source git but as an enterprise DevOps platform). For example it is very easy to define secrets to use via action workflows. And even use ‘GitHub token’ to do things on behalf of you. Since Microsoft acquired GitHub, they are investing heavily on monetizing it (for organizations not open source). So, lots of good features are added literary everyday. But it seems GitHub is not there yet, there are really scary security flaws around for organizations.
GitHub secrets
GitHub secrets are encrypted strings you put in your GitHub repository or organization to pass it to your GitHub workflows actions when they are running. You create them from Settings > Secrets. So if you have for example a third party API that does something with your workflow, you need to put the API access information to secrets.
For example in case of SonarQube to scan your pull request you set the URL and Token as secrets. Then within your workflow, you access them like ${{ secrets.MySecret }}.
The good thing about it is that GitHub has a protection mechanism that if you log the GitHub secrets it is changed to *** , and GitHub UI doesn’t reveal them either . You can feel safe about it; seems no one can retrieve! It might mislead you so you feel safe to put sensitive information in GitHub secrets, but please don’t! I am going to tell you why in a second.
GitHub token
GitHub token is actually a short lived token generated by GitHub. It can do things on the repository without needing your username and password. For example, when we run checkout action (uses: actions/checkout@v2 to checkout our repo to the agent running the workflow), it is actually internally uses github_token. You can use this token in your workflow like ${{ secrets.GITHUB_TOKEN }} or if you are writing an action (like the checkout) you can use it like github.token instead of secrets.GITHUB_TOKEN. This is really powerful, and an unleashed powerful thing is a recipe for disaster! I am showing you why in a moment.
Security flaw of GitHub secrets
Ok let’s get to the part I tell you how things can go wrong. Just before you panic, we assumed that you have contributors in your repository that you don’t fully trust. They can contribute directly to your repository but you protect your main/master branch. This way, you make sure they don’t merge code they should not with your code base. You also have your secrets encrypted and safe on GitHub. So you really feel comfortable that your secrets are safe with GitHub and cannot be decrypted and retrieved. But you know that if you can pass the secret to your third party you can pass it to any third party! As a matter of fact, you don’t even need to do that! Just put a spaces between characters of your secret and GitHub reveals your secrets in the logs (please don’t do that!):
echo ${{ secrets.MySecret }} | sed -e 's/\(.\)/\1 /g'
So a contributor with write access can make a new branch and make a new workflow on that branch (or even change an existing one) and retrieve the secrets using echo command and sed. Not much of a secret right?
name: Retrieve secrets
on:
push:
workflow_dispatch:
jobs:
log-secret:
runs-on: ubuntu-latest
steps:
- name: Run a multi-line script
run: |
echo ${{ secrets.MySecret }} # prints ***
echo ${{ secrets.MySecret }} | sed -e 's/\(.\)/\1 /g' # prints the secrets with spaces
Don’t let github masking a log file gives you a false feeling of safety. As you see at the last line above a malicious contributor can retrieve your secrets easily.
The Solution for keeping GitHub Secret safe:
Good news is we can limit GitHub secrets to protected branches. There is a concept of environmental secrets on repository settings (Settings > Environments). It has a feature so you can you can limit the branches that can use environment secrets using Deployment branches. You can select protected branched or specific branches ex. main. This way the workflow action running on other branches cannot access your secret. Of course GitHub environment is meant for deployment, so if you are using it to protect your sensitive secrets for any things other than a deployment, you are doing a workaround!
A disaster called github_token
The ‘GitHub Token’ is both good and evil; without it you would need a PAT token to do the simplest things (like checking out code on the runner) the problem is that it has too much power. For example you can use this workflow to impersonate your colleagues and push code on their name (much easier for a malicious piece of complicated code gets approved when it comes from your organization well known code hero!)
name: change and commit readme
on:
workflow_dispatch:
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- shell : bash
run : |
echo " Add this to readme!!" >> README.md
git config user.name YourFavoritUserName
git config user.email [email protected]
git commit -am "auto pushed by githubToken"
git push
Code above adds a Add this to readme!! to readme of the main branch and commits it as any name. It might blow you mind to know it does it even if the user does not exist. You might think we protect our main branch so we are safe. Well, protecting the main branch saves us a lot of trouble, but you can still impersonate and commit to a feature branch.
Unfortunately, this is only the tip of the iceberg, github_token can do a lot more! For example, can make a new release with malicious code and it even don’t need to commit that. Here is all the access github_token has, and we are mostly worried about the write access. Fortunately, as you can see as long as we stick to the forking for contribution we are pretty much on the safe side.
Scope | Default access (permissive) | Default access (restricted) | Maximum access by forked repos |
---|---|---|---|
actions | read/write | none | read |
checks | read/write | none | read |
contents | read/write | read | read |
deployments | read/write | none | read |
id-token | read/write | none | read |
issues | read/write | none | read |
metadata | read | read | read |
packages | read/write | none | read |
pull-requests | read/write | none | read |
repository-projects | read/write | none | read |
security-events | read/write | none | read |
statuses | read/write | none | read |
But if you add people or teams as contributors with write access to your repository (manage access), you should be aware of the risk you are introducing.
Summery
Generally GitHub is very safe, don’t get men wrong. And for most cases these security problems are not a bit deal, specially the open source and smaller organizations. But as we sow both GitHub secrets and github_token has security flaws. While GitHub secret is reservable using workflow on any branch you can use environmental secrets to limit the sensitive information to protected branches workflows. Also, GitHub Token has too much power and can make unwanted changes so a bad person that can make workflows on your GitHub repository can make changes to your code or your release packages and artifacts using that token.
Leave a Reply