Today we talk about security in GitHub, and specifically about how to properly use and configure GitHub Actions Secrets.
This article is part of a new GitHub Security Hardening series, so stay tuned for new articles about this topic.
Also, if you are new to GitHub Actions, I recommend you to check the intro video I made about the topic, there I cover all you can do with GitHub Actions.
As usual, if you are a visual learner, or simply prefer to watch and listen instead of reading, here you have the video with the whole explanation.
If you rather prefer reading, well... let's just continue :)
We hopefully all agree that sensitive values should never be stored as plain text in workflow files, but rather as secrets.
Secrets can be configured at the enterprise, organization, repository, or environment level, allowing you to store sensitive information in GitHub, as I explained in this video.
Storing these secrets is not a problem as everything is safely managed by GitHub. They are even encrypted on the client side before reaching GitHub! However, using them can be problematic.
To help prevent accidental leakages, in fact, GitHub redacts any secrets that appear in run logs and replace their values with stars (
This redaction looks for exact matches of any configured secrets, as well as common encodings of the values, like for example Base64. There are however multiple ways in which a secret value can be transformed, and because of that this redaction is not 100% guaranteed.
As a result, there are some things we can proactively do, and good practices we should follow, to help ensure secrets are redacted and to limit other risks associated with secrets.
First of all, if possible don’t use structured data as a secret. Using structured data can cause secret redaction within logs to fail, as redaction relies on identifying an exact match of the secret value.
For example, avoid using a blob of JSON, XML, YAML, or similar as secret value, this significantly reduces the probability that the secrets will be properly redacted.
Instead, create individual secrets for each sensitive value.
Again, this is not always possible, but doing so greatly reduce risks.
Another important step is to register all secrets used within workflows. If a secret is used to generate another sensitive value within a workflow, that generated value should be formally registered as a secret. This ensures that the value will be redacted if it ever appears in the logs.
For example, when using a private key to generate a signed JWT for accessing a web API, it's important to register that JWT as a secret. Otherwise, the JWT won't be redacted if it ever enters the log output.
It's important to note that registering secrets also applies to any sort of transformation or encoding. If your secret is transformed in any way (for example with Base64 or URL encoding), be sure to register the new value as a secret as well.
Let me know in the comments down below if you like me to produce an article and/or a video on how to register anything as a secret in GitHub Actions.
In addition, it is important to audit how secrets are handled. This involves reviewing how secrets are used to ensure they are being handled properly.
To do this, you can review the source code of the repository executing the workflow and check any actions used in the workflow. For example, make sure that secrets are not sent to unintended hosts or explicitly printed to log output.
After testing valid and invalid inputs, view the run logs for your workflow and confirm that secrets are properly redacted or not shown. It is not always clear how a command or tool you are invoking will send errors to
STDERR, and secrets may end up in error logs. Therefore, it is good practice to manually review the workflow logs to ensure that secrets are properly handled.
Next, it’s a good practice, and not only for GitHub Actions Secrets, using credentials that are minimally scoped.
Ensure that the credentials used in workflows have the minimum privileges required. Keep in mind that any user with write access to your repository also has read access to all the secrets configured in it.
Actions can access the
GITHUB_TOKEN from the
github.token context and you should ensure that this token is only granted the minimum required permissions. It is good security practice to set the default permission for
GITHUB_TOKEN to read access only for repository contents.
The permissions can then be increased for individual jobs within the workflow file, as required.
Another thing we should all do when working with secrets is to audit and rotate registered secrets.
For example, we can periodically review the registered secrets to confirm they are still required and remove those that are no longer needed. I personally set this as a recurrent task for my team so every months we have a review of all secrets.
We should also rotate secrets periodically to reduce the window of time during which a compromised secret is valid.
And final good practice, consider requiring review for access to secrets.
Not everyone may be aware of this, but you can use required reviewers to protect environment secrets. A workflow job cannot access environment secrets until a reviewer grants approval.
This is of course only available in GitHub Enterprise or on public repositories, but it is something you should definitely do if you can.
Let me know in the comments below if you have any other tip and best practices when working with GitHub Secrets.
Also, check out this video, in which I explain how to properly store Secrets in GitHub.
Like, share and follow me 🚀 for more content: