Seems to be the general practice these days is to store secrets (e.g., DB, API credentials) in a .ENV file then load it to $_ENV and $_SERVER automatically. This popular library does that and it's even encouraged as best practice. This library takes it a step further and encrypts the value inside the .ENV file.

They say the whole purpose is to easily manage dev/production configs and to minimize config files being pushed to version control. On top of "never store sensitive credentials in your code".

One glaring issue about this is a simple injection of print_r($_SERVER)or print_r($_ENV) in your code will reveal everything. Or, if you forgot to delete phpinfo.php, all is lost! There are also other ways it could leak (e.g., logs).

I'm quite baffled that this practice is prevalent or maybe I'm just missing something.

Is there anything particularly wrong with the good old config.php stored outside document root? I mean you could put that in git ignore too and it's not going to be part of your code at all. You can even encrypt it too. Why use .ENV?


Here's an article that agrees to my sentiments

  • Possible duplicate of Why is storing passwords in version control a bad idea? – Conor Mancone Dec 6 '18 at 17:28
  • @ConorMancone It's not a duplicate. This question specifically questions the methodology of storing credentials in environment variables. That other question is about storing credentials in version control. – IMB Dec 6 '18 at 17:33
  • It sounds like you're thinking of security in a binary fashion. The point of separating secrets, configs and code is to minimize security impacts, not create perfectly secure applications, which is impossible. Also, separating your code from your config is generally considered "good practice" in other programming languages. It's relatively common to include multiple languages in a project, and having a language-neutral secrets and config is normally much preferred than putting configs in code. – Steve Sether Dec 6 '18 at 18:16
  • @SteveSether language-neutral secrets this is the only benefit I see from this. Other than that, it's high risk in my view. There's just too many possible leaks vs accidental version control commit. – IMB Dec 6 '18 at 18:27
  • 1
    Possible duplicate of serverfault.com/questions/892481/… from Server Fault. – Austin Hemmelgarn Dec 6 '18 at 20:03

To clarify, the security and flexibility are gained by putting secrets into environment variables.

.ENV is a convenience and, ideally, is not used in production. Many hosting services will handle deployment for you, and provide a way to store production environment variables securely, without .ENV.

Putting secrets and configuration in environment variables is about mitigating the damage of more common breeches, eliminating classes of mistakes which can lead to security vulnerabilities, and making deployment simple and flexible.

Secrets are put into environment variables so they never touch disk when deployed. Only the server process and its children have the secrets. This mitigates the damage if an attacker...

  • gains read-access to your production server's filesystem
  • gains read-access to your code
  • can execute code, even your code, on your production servers

To get your environment variables, the attacker must be able to alter code on your production server, or inject it into your deployment process. If someone can inject print_r into your production, or if you forgot to delete phpinfo.php, a great many things have gone wrong with your release procedure.

Practical security is layered. If one layer is breached, the rest are still secure. If an attacker gains access to your filesystem, they don't have your secrets. If you're worried they'll alter your running code, put your code on a read-only filesystem.

Configuration is put into environment variables to simplify deployment. One does not need to have separate config files for development, and testing, and staging, and production, and each customer. Instead, each deployment sets its own environment variables.

If you're following Point 10 of the Twelve Factor App, different environments and deployments should have few differences.

.ENV is a optional compromise for convenience. Developers will be running bits of code in many different environments, and remembering to set environment variables in all of them get tedious. Tedium breeds mistakes. So it's better to have a fairly secure process, and all the secrets in a single location.

Development machines have different attack surfaces than a production machine. For example, they can be stolen. One of the important security layers is to ensure developers use an encrypted filesystem, encrypted virtual memory, and screen locks; if their machine is stolen, the information on the disk is safe.

Even so, .ENV should not contain production secrets. Developers should be using development databases and accounts.

If .ENV is used for deployment, then it should be encrypted. Then one must only secure a single, per-developer key which is put in, you guessed it, an environment variable. Rails Encrypted Credentials uses this approach.

Is there anything particularly wrong with the good old config.php stored outside document root?

There are many disadvantages.

First and foremost, your secrets must live on your production server's disk. Anyone with read access to that file now has your secrets. Putting it outside document root is little protection, there are many security vulnerabilities which allow access to files outside document root.

config.php is code and is vulnerable to additional classes of attacks that a data file is not.

config.php can get large and complex making it difficult to tell which parts are secrets and which are not. This makes security audits difficult.

config.php requires putting your secrets on disk. .ENV is optional, you can develop and deploy using only environment variables.

config.php can easily find its way into the code repository.

Each deployment needs a slightly different config.php with different secrets making deployment more complex.

In contrast, .ENV is a small, simple data file with a single purpose. Your QA and deployment process can automatically detect if .ENV slipped in, or if it is unencrypted. There is a single, simple data file which needs to be changed between environments.

And .ENV is entirely optional.

I mean you could put that in git ignore too and it's not going to be part of your code at all.

config.php is code and should be tested and version controlled. Not having it as part of your repository complicates deployment. This is point 1 of the Twelve Factor App. .ENV minimizes the compromises to your development process. And, unlike config.php, it is optional.

There are also other ways it could leak (e.g., logs).

You are correct that logging is a potential security leak. That doesn't mean we give up.

You can set different log levels in development vs production using, you guessed it, environment variables. Production log levels would log less and be more careful about what is logged.

You can add a scrubber to your logging function, there are many available, to remove any sensitive information before it is logged. Some look for common patterns such as passwords in URLs, credit card numbers, and PII. With all your secrets in .ENV your logger could automatically remove secrets with a simple regex.

And rather than logging to a local file, you can also stream your logs to a logging service. This is point 11 in the Twelve Factor App. Now your logs are off-site; your production server can be compromised but your logs are still safe.

The Twelve-Factor App takes some getting used to. PHP is an older language with more traditional ideas and may require more adaptation than other languages. I've been following it for a couple years in Ruby. It's made a marked improvement in the speed and reliability of my processes.

And I hope now you understand why Environment Variables Considered Harmful for Your Secrets is itself harmful. The author is concerned about exposing secrets to child processes, a valid concern, but then advocates putting those secrets on disk; a far broader exposure. And since child processes are run by the same user as the parent, the child processes can see the secrets file anyway.

If you truly don't want to store any secrets locally at all, look into Vault.


No Inherent Problems from a Security Perspective

If your application or its server is compromised, the attacker will likely be able to read everything in the config, including sensitive data such as passwords, tokens, or connection strings. This is true whether you use config files or environment variables.

If you ever suspected a compromise, you would scrub/reset your secrets regardless of whether your config is stored in files or environment variables.

Practical Reasons

The article you linked as an explanation offers practical reasons for using environment variables.

According to their recommendation, this approach reduces the risk of: bad commits (specifically, those containing config data), misconfigured deployments, and poor development practices.

To the extent that their recommendation prevents bad security behaviors, it is best to follow it. Security lapses are due to human error during deployment/maintenance as well as software bugs. If an automated process or a habit prevents a problem, it is the right thing to do.

  • this approach reduces the risk of: bad commits As I mentioned, you can also put config.php in your ignore file. I don't know but I still don't buy it. The security issues I have pointed out is a much higher risk than a bad commit for me. – IMB Dec 6 '18 at 16:32
  • From your example, if people can inject like that, then you need to redeploy at the OS/container level and reset your passwords regardless. Dumping environment variables may be easier than finding a config file, but at that point you can't really trust anything the application can access anyway. That config file is not separated by a security barrier which would restrict the attacker in your scenario. – DoubleD Dec 6 '18 at 18:54
  • Isn't one of the tenets of security is to make it harder for an attacker? Dumping ENV vs dumping config.php is like rainbowtabling an md5 hash vs bcrypt. The former is easier, the latter requires more guesswork. Also, how do you defend against accidental logging or output of environment data? E.g., if someone leaves whoops enabled in production, your credentials are there for the taking just like that. – IMB Dec 6 '18 at 19:04
  • Your approach is security through obscurity, i.e., hoping the attacker won't know where to look. Unless there is a security barrier between the attacker and the config, the benefit is minimal. You seem to think locating a config file is a huge obstacle for an attacker; it is not. You're quibbling over a slight delay. If an attacker can shell out of your application, you need to fix the problem, sanitize everything, and redeploy. – DoubleD Dec 6 '18 at 19:24
  • Your approach is security through obscurity It's not my primary approach by any means but a welcomed benefit. I'd take a few minutes delay vs 1 sec of var_dump($_SERVER). Anyway that's just one issue among others. BTW I updated my OP with an article I found for more info on why I think this is an issue – IMB Dec 6 '18 at 19:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.