Twelve-factor app anno 2022
xenitab.github.io
xenitab.github.io
> Note that environment variables are not suitable for passing secrets (such as passwords, key material, …) to service processes. Environment variables set for a unit are exposed to unprivileged clients via D-Bus IPC, and generally not understood as being data that requires protection. Moreover, environment variables are propagated down the process tree, including across security boundaries (such as setuid/setgid executables), and hence might leak to processes that should not have access to the secret data.
The former reason is systemd-specific, but the latter isn’t. There are some more reasons in https://movingfast.io/articles/environment-variables-conside....
You should always use both - keep passwords in secret manager and during build communicate them to relevant parts of app via env vars. That way, you can easily reproduce it locally without secret manager available or slowing you down (maybe you need VPN to access it?, GitLab access to secrets etc.). Also, you can't easily change them for testing purposes or store variations of them - perhaps you mix several production with several testing environments. Contrary to that, you can keep dozen of them and easily change them in a single file while developing.
Thee are bunch of tools to hide leaking environment vars too by masking them.
So, no, this is not obsolete. Maybe it doesn't make sense in some scenarios but such cases shouldn't be the norm.
Shuttling secrets via env vars expose you to strictly more risk vectors than through properly permissioned files on disk. Point blank, no argument. It's not acceptable to use env vars for secrets.
But... it'd be cool if we had better support for avoiding accidental leaks. E.g. a way to identify another process as my user that ends up reading /proc/<pid>/environ unexpectedly. You could model that with SELinux but it could be a bear to own and not worth it for outside of large/important/high-value target companies. I suppose that's an apt description of SELinux generally...
Maybe we could have a "light" version at the code level, maybe use the type system to throw an exception if you ever try to .toString() a secret. I'm trying to think how i would do that in Java without binding someone to an awkward type hierarchy (yuk!). This is not (currently) valid Java:
interface Secret {
default sealed String toString() {
throw new IllegalAccessException()
}
}
By this i mean a type that if i compose with, then it overrides (not currently supported) my toString() and blocks me changing the impl of that (sealed), then i can't accidentally log it for example.I think the way to do this in Java today would need to be via a dedicated class, i don't think i could use a composable interface. I mean that's fine-ish but i feel allergic to constraining other developers to my
abstract class Secret { ... }
EDIT: just blocking /proc/<pid>/environ wouldn't be enough, the other process could ptrace(2) for example.I don’t think the vulnerability you mention here is “worth working around” for most apps, where you can trust the hardware and OS you’re running on. For those that can’t trust the OS and hardware they are running on, this may be legitimate. Even then, if you can’t trust that, what are you doing running software on it?
PHP's phpinfo() function comes to mind immediately: https://www.php.net/manual/en/function.phpinfo.php
It's a good question.
But consider, containers are (in modern infra) transient and usually sit in a constrained security context where access to other resources is pretty limited. Meanwhile the login credentials and security tokens are often long-lived and (usually) give broad access to a wide security context.
If you give me a back door to a container, the first thing I would be trying to do is use it to level up my credentials before the container dies. One of the first things I would try is dumping the environment!
But if the secrets were learned via an ephemeral file or through a temporary network connection, and even better if they were exchanged for time limited or single use type tokens, I am really left with both a challenging task to dig them out and / or something of limited value anyway.
In any case, defense in depth seems like reason alone to do this.
Then the blame is on the logging system configuration, not the env vars. Like you sanitize sensitive information out of logs, you should sanitize and not expose environment variables in your logs.
But also note, in such a case the key probably isn’t coming from an environment variable, more likely is a subkey generated by a HSM.
This allows you to keep permission grants separate, and you can safely send “.env” files and (gasp) save them in version control.
You can change where and how you store and encrypt them, rotate keys etc. all transparently.
I'm not parent commenter, but that's how I've done it before. The file on disk has the appropriate permissions etc.
I'm sure there are issues with this, so if anyone can enlighten me I'd be interested.
> This factor is now obsolete in one respect. As much as possible, secrets (passwords, private keys, et c) should be stored using a secrets management system such as Hashicorp Vault or Azure Key Vault. Particularly where we can rely on the infrastructure to authenticate the calling process (e.g. via a Kubernetes service account) access to secrets will not directly require credentials. The existence of the secret is under version control, but the actual secret content is immaterial.
You can store secrets in a process belonging to a different PID and get them with some kind of RPC.
It can get it from a file that isn't readable by the user that runs remote-accessible code.
> Our solution thus has been to use a configuration file which is managed by the server's configuration management software (chef in our case). This way we can store secret keys outside of the code repository, and manage them using the same tool used to configure our servers. This solution, while not perfect, doesn't expose our secret keys to child processes and requires explicit access, which also communicates how developers should work with it.
I honestly don't know how so often now smart and somewhat experienced people don't have any confidence in their abilities over some random list on the internet, especially since when I forced them to think for themselves they do come up with improved approaches.
But I think you're right about the more mid-experience developers. It would be nice if things like the 12factor specification weren't driven by Heroku's desire to make people feel like their approach is right, but rather, had a more nuanced approach like "these are our guidelines, we're confident in most cases they should be followed. If you fully understand a guideline and have a reason (driven by the bottom line, not subjective tastes, and not mentioned by us) to avoid following that guideline, do not follow it"