Pre-Requisite
Basic understanding of .env files.
Insight
I am in favour of the idea that the contents of the .env file should be committed to version control. The trouble is that blindly committing it creates a huge security risk. Here I discuss a few pointers on how to do so in a safe manner.
Committing .env File To Version Control
Pros
- All the variables are tracked - creating, updating, and deleting variables are easy.
- Easy deployment - corollary to above, deployment is easy because every variable you need is in the repository.
- All the benefits that come with version control such as history, revert, comparison, etc.
Cons
- Security Risk - your secrets are exposed to anyone who has access to your repository (both public and private).
- All the drawbacks that come with version control, mainly history - your secrets will live forever in your repo unless you edit past commits or nuke all your history.
Despite the security concerns, the benefits are too great to not commit. Below are some of the best practices to follow to keep your secrets safe while committing .env variables.
Best Practices
Suggestion 1: Create + Only Commit The Template .env File
The best way to protect your secrets is to never commit them in the first place. But I still want to be able to track which variables are needed so that all developers are on the same page. For this reason, I recommend creating a template .env file.
Consider a sample .env file's content below:
VERY_IMPORTANT_SECRET = KNDSAIJOFNEWHKFNERKJGNBFDSHFNDSFNDSKJ
NOT_VERY_IMPORTANT_SECRET = NFDSIJNFJDN
RANDOM_VARIABLE_1 = FNDSJANFS
RANDOM_VARIABLE_2 = MFDKSJANFEJWF
Now create a near identical file called .env.template with the following content:
VERY_IMPORTANT_SECRET =
NOT_VERY_IMPORTANT_SECRET = NFDSIJNFJDN
RANDOM_VARIABLE_1 = FNDSJANFS
RANDOM_VARIABLE_2 = MFDKSJANFEJWF
The trick is to NEVER commit your actual .env file but commit your .env.template file that only contains non-sensitive information. .env.template file serves to tell all the developers that such variables exist and need to be filled in, that's it. In this case, VERY_IMPORTANT_SECRET value was left blank.
The blank values should be filled in during the developer's setup (typically this is done by duplicating .env.template, renaming it as .env, and then getting the values from other developers or a setup guide). If any changes are needed to be made in the .env file (for ex. naming), they should be mirrored in the .env.template so that all devs are notified of the changes.
Suggestion 2: Utilize Cloud Service
This is meant to complement Suggestion 1 above. The shortcomings of Suggestion 1 is that we are unable to commit important prod and staging env variables. This can be worked around by preparing special .env files only meant for staging and production and uploading that to the cloud services.
If you are currently using cloud service providers (gcloud, AWS, Azure, etc), most of them offer tools to manage your variables. For example in Azure DevOps, you can upload .env files in Pipelines/Library to be used during your CI like the picture below:
You can take a step further and store .env.staging and .env.prod to target different environments. Your file will only be as secure as the security rules you define for it, so please make sure you configure them as needed.
Suggestion 3: Encryption
You can also choose to encrypt values that can only be decrypted by the production servers. This means you may get away with committing all production-level encrypted variables to version control.
However, some caution needs to be advised on how to handle the private key and where you are decrypting them (server vs client). They should only be decrypted on the server side. If done right, we can harness the full potential of version control by committing all information to version control.