Securely handling credentials in web applications is not trivial. Version control systems like Git and Mercurial allow us to share code with our peers, work collaboratively on a single web application, and track our changes as we update our codebase. They act as an ever present ledger, noting down every piece of work you do and allowing developers to go back and forth in time between commits.
This is fantastic if you are wanting to roll back a broken deployment or understand why a section of code was introduced to an application. However, this unbroken history also has a major drawback... it is very hard to edit the ledger and erase something you didn't want to be included.
GitHub has a page on their website dedicated to removing sensitive information that has accidentally been committed to a repository. They recommend using
git filter-branch or the open source tool, BFG Repo-Cleaner to remove your sensitive information but both of these tools come with a big caveat; you will be unable to prevent this data from being visible in clones and forks of your repo.
In most situations, it is easier to just change your passwords or keys and change your workflow so that a similar leak of your sensitive credentials doesn't occur again. One such approach would be to store all of your sensitive credentials in the application environment.
In 2011 developers from Heroku developed the Twelve-Factor App Methodology, a list of best practices for portability and resilience of web applications. Heroku recommend you should store configuration differences between deployments in the applications environment.
When moving between your production, staging, and testing platforms, you will be using different database credentials so, following the twelve-factor app methodology, you would move all database connection details into your applications environment.
In PHP this usually means setting up the credentials as environment variables in NginX or Apache, and rewriting your PHP files to load the credentials from the environment instead of a config file or in a class. For example, your config might have once looked as follows:
<?php $config = [ 'db_user' => 'terry', 'db_password' => 'nutkins', 'database' => 'really_wild_show', ];
Using environment variables, your config would be rewritten to something like the following:
<?php $config = [ 'db_user' => getenv('DB_USER'), 'db_password' => getenv('DB_PASSWORD'), 'database' => getenv('DB_NAME'), ];
You could set these environment variables in Apache by adding the following to your virtual host config:
<VirtualHost hostname:80> SetEnv DB_USER terry SetEnv DB_PASSWORD nutkins SetEnv DB_NAME really_wild_show </VirtualHost>
Advantages Using Environment Variables
By taking this approach, you should never commit your sensitive credentials to your repository, as they are never stored in files in your web application.
Setting up virtual hosts and setting environment variables in NginX and Apache can be cumbersome, especially when developing. Luckily, there are packages available for PHP which make this process a lot easier.
Vance Lucas created a dotenv package that lets developers quickly scaffold environment variables by adding them to a
.env file that should be added to your
.hgignore file to stop it being committed to your repository. The
.env file specifies key value pairs that are loaded into
$_SERVER automatically, simulating the environment you would use for your application in production.
The question of whether this approach is more secure is hotly debated. On the plus side you are:
- Reducing the likelihood of committing this information to your repository
- Reducing the likelihood of sensitive credentials being stored on a developers computer
- Improving the portability of your application among different platforms such as production, staging, and test.
However, some argue that you are simply moving the issue of leaked credentials to a different domain but not removing it entirely. Some drawbacks are:
- It is impossible to track who/what has accessed these credentials
- Environment variables can be passed to child processes meaning they can be exposed accidentally
- Debugging tools can sometimes capture and display environment variables
- Some applications will log environment variables in their log files for debugging purposes
Phpinfo() Exposing Credentials
A good example of accidental exposure in PHP is when developers use the
phpinfo() function to quickly determine which version of PHP they are running or which PHP extensions are installed.
On the resulting page, there is a section called PHP Variables which will print out your environment variables. If you use this function on a production site, you will be exposing all of your credentials to the outside world to see.
Despite all the drawbacks, I still believe setting your credentials in your environment is better than storing it in a config file. In a well managed server environment, the risks of exposing your environment variables should be minimal.
Time and time again, I have seen examples of developers accidentally pushing their passwords or keys to public GitHub repos. This might be because commit contents aren't always immediately apparent, especially if using the command line. It could also be because developing web applications is typically a collaborative endeavour, and it only takes on developer to make a stray commit, to risk the whole integrity of the application.
Because of this, I will err on the side of caution given my experience, and use environment variables over file configs in all of my applications.