Hackers gaining access to your application’s code can be devastating for proprietary software, but storing credentials in Git can give them elevated database access. Even if you don’t plan on your Git repository getting hacked, it’s good risk management to minimize the potential damages.
Source Control Access as an Attack Vector
Open source software isn’t insecure; after all, much of the software used to run the Internet is developed publicly on GitHub. While having the code public means hackers can find exploits more easily, open source has proven that it can be great for transparency and community bug-fixing.
However, this doesn’t mean that closed source software is inherently more secure. When developers assume that the source control is private, they often take the easiest solution when managing secrets like API keys or database credentials: hard-coding them into the application rather than using proper secrets management.
One of the major attack vectors for hackers is gaining access to source control, and scanning projects for API keys or other secrets that shouldn’t be there. This lets them elevate their permissions and attack the rest of the network, often gaining access to sensitive customer data or databases.
Recently, the TSA “No-Fly” list was leaked via this exact method. An insecure Jenkins build server was left open to the public, a common mistake made when setting up software that is supposed to be in private subnets behind access control. On the Jenkins server, the hacker was able to access the build history, which contained the code, and found AWS S3 credentials for the airline that were hard-coded into the application.
Don’t Store Secrets in Source Control
Every application will have different methods for securely storing API keys and database credentials, but there are a few common methods.
The first is using config files, which can be deployed in production onto the server, and read by the application at runtime. For example, ASP.NET projects default to using an
appsettings.json file that is empty while stored in source control. It is up to the administrator deploying the application to deploy the production config file.
You can also use environment variables, which are set by the system before running an application. This is commonly used for Docker containers, since it’s an easy way to inject credentials into an already built container. For example, Portainer offers environment variable management as part of its Docker web interface.
Typically, it’s a good idea to rotate secrets regularly, since outdated credentials pose a security risk. One of the tools that can manage rotating secrets is AWS’s Secrets Manager, which acts as a storage system that can easily supply secrets to your AWS applications.
For applications outside AWS, or any application using a similar service, there’s a bit of a problem in that you’ll still need an IAM key to access Secrets Manager, which means you’ll need to manage an IAM key, which is the exact problem you started with.
However, AWS’s IAM system allows for discrete management of roles and permissions. You could, for example, create a user for each server you have, and control which keys in Secrets Manager they’re able to access. You can also enforce security policies on the IAM users, requiring them to be rotated regularly as well.
Don’t Store Secrets in Build Scripts
One of the worst security mistakes developers can make is storing keys for production deployments in build scripts used in a CI/CD pipeline.
For example, you might have a shell script or YAML file that is used to control building, testing, and, most importantly, deploying your application to your servers or a storage service like Amazon S3. However, if an attacker was to gain access to this script, they would be able to deploy anything to your servers or storage buckets.
One of the most common ways around this is by using a secrets management tool like GitHub Secrets, which stores credentials in your repository that can be accessed by name in GitHub Actions build scripts.
If your application is automatically deployed using GitHub Actions, and needs credentials to function, you can also use Secrets to inject them into the build process at runtime, either by creating the necessary config files, or setting the necessary environment variables. This way, your repository could even be public and still build an application that gets injected with production credentials before deploying.
GitHub Secrets created within an organization can even be shared across multiple repositories, providing an easy way to centrally manage secure keys. Other build server tools like Jenkins or TeamCity will also have built-in secrets management.