3

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?

Update:

Here's an article that agrees to my sentiments

IMB
  • 2,888
  • 6
  • 28
  • 42
  • Possible duplicate of [Why is storing passwords in version control a bad idea?](https://security.stackexchange.com/questions/191590/why-is-storing-passwords-in-version-control-a-bad-idea) – Conor Mancone Dec 06 '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 06 '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 06 '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 06 '18 at 18:27
  • separating code from config allows you to do things like ignore .env files in your IDE, so it's hard to screw up and check it in. I'm not a PHP developer, but does everyone in the PHP world always use config.php to exclusively store configuration details, not store ANY code in it, and NEVER use any other file to store secrets? Conventions like separating code and config make it harder to screw this stuff up. Stick to the KISS principle (keep it simple, stupid). – Steve Sether Dec 06 '18 at 18:49
  • @SteveSether there's no functional "code" inside `config.php`, it's usually juts a list of constants. And it's "separate" because it's outside root directory. The only difference is one is named `.ENV` and the other `.php`. The former is susceptible to the issues I pointed out in the OP while the latter has 0 security issues other than accidental version control commit which can also be a problem with .ENV. – IMB Dec 06 '18 at 18:54
  • In fact, it does answer your question, although "why" it answers your question may not be obvious. The reason people store things in a `.env` file is specifically because these files are not meant to be stored in source control. The point of storing in a `.env` isn't to avoid storing it in a `.php` file, but simply to keep build/environment configuration data out of source control. Since it is configuration data and not code, it makes more sense to store it in something other than a `.php` file. However, the overarching concern is keeping config data out of source control. – Conor Mancone Dec 06 '18 at 19:02
  • @IMB oh, I see, you literally want to know why not use a `.php` instead of a `.env`. In that case it doesn't matter. There is exactly zero difference between the two. If someone has managed a remote code execution vulnerability on your server then they will be able to get everything out of your `config.php` as easily as they would out of your `.env` file. The first thing I would do after accomplishing a remote code execution attack is to download 100% of your source files and your database. You are owned either way. `config.php` provides no protection. – Conor Mancone Dec 06 '18 at 19:05
  • @ConorMancone Okay then how about the fact that some loggers may log or output ENV variables? Or leaving `phpinfo.php` in production? These are much more prevalent than code injection. – IMB Dec 06 '18 at 19:07
  • Perhaps I should just write an answer, but I think you're misjudging the situation. The over-arching concerns are keeping secrets out of source control and allowing you to change deployment configuration from one environment to another. The PHP library you linked to has a specific way of doing it that could in fact conflict with how some loggers operate (leaving `phpinfo()` in production is a much more boneheaded move that has large ramifications anyway). Sure, depending on how your system operates, you may not want to use that exact implementation, but the overall goal is definitely good. – Conor Mancone Dec 06 '18 at 19:15
  • @IMB I.e. I would focus more on the overall goal and less on the specific implementation. If a given implementation doesn't work for you then use another one. I actually happen to use that exact library myself, and while I might prefer not to pollute my `$_SERVER` variable, I also know that using it poses no risks to my application. – Conor Mancone Dec 06 '18 at 19:17
  • @ConorMancone The purpose of my question is to put weight on why it's okay to pollute `$_SERVER` with sensitive data. What I gather is that it's a risk some people are willing to take. Note: I added an article in my OP. – IMB Dec 06 '18 at 19:27
  • @IMB every decision is a matter of deciding what risks you are willing to take. By the simple virtue of building a web application you have already made a decision that has added risk to your business (or whatever you are doing). That's just the nature of the beast. Nothing is ever perfectly secure. Instead we all decide what level of risk we are willing to accept in return for actually getting stuff done. The only risk-free business decision is to not start a business in the first place. – Conor Mancone Dec 06 '18 at 19:40
  • @ConorMancone Sorry but that's not really a strong argument. It's you like agreed to what I'm saying that it is in fact insecure than good old `config.php`. – IMB Dec 06 '18 at 19:54
  • Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/86736/discussion-between-conor-mancone-and-imb). – Conor Mancone Dec 06 '18 at 19:57
  • 1
    Possible duplicate of https://serverfault.com/questions/892481/what-are-the-advantages-of-putting-secret-values-of-a-website-as-environment-var/892506#892506 from Server Fault. – Austin Hemmelgarn Dec 06 '18 at 20:03
  • @AustinHemmelgarn That's quite right. The highest answer there lays out the two sides nicely. – IMB Dec 06 '18 at 20:16

2 Answers2

3

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.

Schwern
  • 1,549
  • 8
  • 17
0

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.

DoubleD
  • 3,862
  • 1
  • 6
  • 14
  • `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 06 '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 06 '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](https://github.com/filp/whoops) enabled in production, your credentials are there for the taking just like that. – IMB Dec 06 '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 06 '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 06 '18 at 19:34
  • There is a potential gotcha there---you have to spawn child processes the right way, as they often inherit access masks from the parent. As the first commentor there suggests, you can unset the variables after reading them if you're concerned about that. The bottom line is that this guidance relates to managing your secrets safely during development. Either approach is sensible. In your shoes, I would follow guidance specific to your language or framework if available---barring a compelling reason to do otherwise (e.g., when things are known to be broken). – DoubleD Dec 06 '18 at 20:47