Protecting Against Supply Chain Attacks
Overview
Modern software applications rely heavily on dependencies to provide core functionality. The software ecosystem relies heavily on CI/CD to publish source code and binaries to public repositories. Together, this gives the opportunity for malicious actors to circumvent standard security measures and attack supply chains directly, allowing them to infect many applications and websites simultaneously.
Continuous delivery providers, like CircleCI, understand these risks. We know we are the holder of many keys, and we go to great lengths to protect the credentials you use to publish and deploy software. No CI/CD-as-a-Service provider can guarantee safety, and it is possible to use these platforms in insecure ways.
Minimizing risk as a publisher
As both a downstream user or publisher of software, you can protect yourself and your users using a few tricks.
Using contexts
When using CircleCI, you can split credentials and secrets into multiple contexts that can be used individually or combined in a build step. Avoid putting everything into the org-global context. This means that if there is a security error in one build step, only a small subset of your credentials are exposed. This effort is known as the principle of least. As an example, the step where you download dependencies and execute their build scripts should not have access to your deploy keys simply because nothing in that step needs them.
Additionally, you can put sensitive contexts used for deploying and signing software into restricted contexts that are governed by your GitHub groups. These secrets are only then accessible to authorized users. Combining this with GitHub branch protection requiring review before merging can help reduce the likelihood of exposing credentials to malicious code.
Minimizing risk as a developer
If you are a developer using software, a significant portion of your dependencies and even tool chain are likely automatically published via continuous delivery.
Pinning Dependencies
Most tools such as yarn, cargo, and pip support the ability to create and use lock files to pin dependency versions and even hashes. Some tools can enforce installation using only packages with versions and hashes specified. This is a baseline defense against bad actors publishing malicious packages with a higher SemVer number, adding malicious distribution types to an existing package version, or overwriting the contents at a given version number.
Consider the following simple method for installing a Python project using pip and pip-tools.
$ echo ‘flask’ > requirements.in
$ pip-compile --generate-hashes requirements.in --output-file requirements.txt
$ pip install --no-deps -r requirements.txt
This adds a single top-level dependency called flask
to an input file, then generates secure hashes for all transitive dependencies and locks their versions. Installation using the --no-deps
flag ensures that only the dependencies listed in the requirements file are installed and nothing else.
Likewise, a similar example will ensure only exactly the known dependencies are installed when using yarn
.
$ yarn add express
$ yarn add express
# during your build
$ yarn install --frozen-lockfile
Many tools for scanning dependency files exist, and many are first party for a given language or tool chain. On CircleCI, there are orbs available that offer dependency scanning, and cron jobs for periodic scanning to ensure your applications are scanning more often than your pushes.
Using dependency pinning with hashes like this prevents malicious binaries or packages from silently replacing known good versions. It protects against a narrow range of attacks where the upstream repository is compromised. This can protect your workstation and CI builds.
Conclusion
CI/CD build systems can be trusted with deploy keys and other secrets, but using them safely still falls on the project maintainers. On CircleCI, it is easy to enforce isolation by splitting secrets into multiple contexts, using multiple build steps, and passing artifacts between steps by using workspace persistence. Security is a team sport, and being careful with your builds helps protect downstream develops and end users.
Help make this document better
This guide, as well as the rest of our docs, are open source and available on GitHub. We welcome your contributions.
- Suggest an edit to this page (please read the contributing guide first).
- To report a problem in the documentation, or to submit feedback and comments, please open an issue on GitHub.
- CircleCI is always seeking ways to improve your experience with our platform. If you would like to share feedback, please join our research community.
Need support?
Our support engineers are available to help with service issues, billing, or account related questions, and can help troubleshoot build configurations. Contact our support engineers by opening a ticket.
You can also visit our support site to find support articles, community forums, and training resources.
CircleCI Documentation by CircleCI is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.