Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I used to store secrets in the FS, and was told the best practice was env vars. Now it's not env vars. What is it then?


There isn't a right answer. It's just that people don't understand that one doesn't provide any meaningful benefit over the other (in the context of storing secrets), but the security "experts" are always eager to claim "X is insecure, do Y instead, it's best practice btw"

Unless I'm missing something, there are three scenarios where this comes up:

1. You are using a .env file to store secrets that will then be passed to the program through env vars. There's literally no difference in this case, you end up storing secrets in the FS anyway.

2. You are manually setting an env var with the secret when launching a program, e.g. SECRET=foo ./bar. The secret can still be easily obtained by inspecting /proc/PID/environ. It can't be read by other users, but so are the files in your user's directory (.env/secrets.json/whatever)

3. A program obtains the secret via some other means (network, user input, etc). You can still access /proc/PID/mem and extract the secret from process memory.

So I'm assuming that what people really want is passing the secret to a program and having that secret not be readable by anything other than that program. The proper way to do this is using some OS-provided mechanism, like memfd_secret in Linux. The program can ask for the secret on startup via stdin, then store that secret in the special memory region designed for storing secrets.


The main security benefit of byzantine paranoid security best practices is that they massively hinder productivity. If you can't make a system, the system will have no vulnerabilities.


lmao so true


You don't even need to roll your own solution with memfd. Linux already has keyrings[1] as a kernel concept.

[1]: https://man7.org/linux/man-pages/man7/keyrings.7.html


I’d wager that–in the context of web apps–over time there have been many more (or more readily exploitable) arbitrary file read/directory traversal/file inclusion vulnerabilities than remote code execution ones, so the preference for having secrets in memory as env vars may stem from that. You’re also probably not reading from /proc/self/mem without code execution either.


Well, if there's an arbitrary file read, shouldn't the attacker be able to just read /proc/PID/environ anyway? It behaves like a regular file in that regard, unlike /proc/PID/mem, which requires seek operations to read data.


Also for 3. you just added another supply chain issue and another thing to maintain and keeping up to date, which is probably worse


Well, I’d be the first to admit that we have a gap here, the solution that I personally would consider ideal doesn’t seem to actually exist, at least on the server-side.

If we are running under something like K8S or Docker, then I think there should be some component that runs on the host, that provides access to secrets over a Unix domain secret, and then we mount that socket into each container. (The reason I say a Unix domain socket, is so then the component can use SCM_CREDENTIALS/SO_PEERCRED/etc to authenticate the containers). I’d also suggest not using HTTP, to reduce the potential impact of any SSRF vulnerabilities (although maybe that’s less of a risk given many HTTP clients don’t work with Unix domain sockets-or at least not without special config). (Can we pass memfd_secret using SCM_RIGHTS?)

For desktop and native mobile, I think the best practice is to use the platform secret store (Keychain on macOS/iOS, Freedesktop Secret Service for desktop Linux, Android Keystore, Windows Credential Manager API, etc). But for server-side apps, those APIs generally aren’t available (Windows excepted). Server-side Linux often lacks desktop Linux components such as Freedesktop APIs (and even when they’re present, they aren’t the best fit for server-side use cases)


The problem with .env files is that you are doing both.

You have a .env file that is in the same directory as your code and you just copy to to env vars at some point. This does not even meet the security principles that dotenv is supposed to implement!

I think people are blindly following the advice "put secrets in env vars" without understanding that the point of it is to keep secrets outside files your app can read - because if you do a vulnerability or misconfiguration that lets people read those files leaks the secrets.

What you can do is have environment vars set outside your code, preferably by another user. You do it in your init system or process supervisor. Someone mentioned passing them in from outside a docker container in another comment.


> people are blindly following the advice "put secrets in env vars" without understanding that the point of it is to keep secrets outside files your app can read - because if you do a vulnerability or misconfiguration that lets people read those files leaks the secrets.

The problem with this is that, on Linux, the environment is a file, /proc/self/environ

And yes, as has been mentioned in some other comments, the process memory is also a file /proc/self/mem - but it is a special file that can only be read using special procedures, whereas /proc/self/environ behaves much more like a normal file, so a lot of vulnerabilities that enable reading /proc/self/environ wouldn’t enable reading /proc/self/mem

Technically one workaround on Linux is to not mount /proc (or at least not in your app’s container) - but doing that breaks a lot of things


Yes... surely the point of .env file is as a shortcut to provide env vars for local development

.env file shouldn't be used in production, env vars should be injected directly


I agree that is what should happen. That is not what dotenv does though: https://www.npmjs.com/package/dotenv#-documentation


I think dotenv would be fine as long as it doesn't raise exceptions if no .env file is found, i.e. if it works just as a helper for local dev and as a no-op for production

I notice the dotenv docs are recommending dotenvx now in places: https://www.npmjs.com/package/dotenv#-manage-multiple-enviro...


I have seen many .env files in production. SOme junior devs told me it was what they had been taught to do.


Not using env vars is security through obscurity. If someone has ssh access to your container, it doesn't matter whether the secrets are on a file or on memory. The attacker has as much access as the app itself.

On the other hand, using .env vars can leak in different ways like a developer mistakenly committing secrets to git or making this file available to the world wide web.


The filesystem is fine, but we really shouldn't be using .env files that get loaded into environment variables due to them leaking in a few different ways.


Pull them from a remote host over ssh? :D




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: