How to handle secrets on the command line
smallstep.com
smallstep.com
Nice article, covers the basics well. Credential files seem like simplest way to go and are secure enough for most local uses. For anything more involved a secrets manager is probably required. I've been using Linux for a long time and hadn't heard about `keyctl`, thanks for mentioning it. A more flexible solution might be https://github.com/mozilla/sops
The downside is that you will need to whitelist every site that you want to remember your login/settings for when revisiting them.
Still, I haven't used a browser extension in years, partly because of extensions like this that require access to all browsing data, even if they're open source like in this case (though the author of "I don't care about cookies" has a strange concept of distribution[1]). The inconvenience of wiping cookies and clicking through cookie consent forms is much more tolerable than allowing random extensions access to all my browsing data.
[1]: https://teddit.net/r/privacy/comments/bru6wd/p/eohtox3/
Cookie pop-ups need to die. Non-essential cookies aren't to "improve your experience", they're usually for invasive tracking, and if a site only uses strictly necessary cookies, even the GDPR doesn't require explicit consent.
While it's not entirely unobtrusive, especially on mobile, it's far better than similar forms on most other sites that usually take up the bottom half of the screen, or block the content entirely behind a modal dialog.
> As well as "Continue to site", it has buttons for "Policies", ...
Those others aren't buttons, but links, and small ones. The most prominent element is the single button that dismisses the dialog and is actually safe to click. If you've seen most other consent forms, rejecting cookies (if possible at all) is usually done via a small link next to a prominent "Accept all" button and other such dark patterns.
So this is a great UX improvement, though I agree with you that all these forms are obnoxious and we should get rid of them. Unfortunately it's the best we currently have to mitigate this abuse legally (at least in the EU).
Also, I'd be fine with some non-essential cookies for e.g. analytics, as long as this is not shared with 3rd parties, so no Google Analytics and such, but few sites implement it that way.
Though TBH all this feels like privacy theater. There are more sophisticated ways than cookies for tracking users that aren't being discussed nearly as much, yet are probably already widely used.
That's exactly how this one works for me. The prominent "Continue to site" is "Accept all" in disguise, and to opt out you need to click on the little "Preferences" button/link. It's the same dark pattern as usual, not a UX improvement.
They just started showing up more often after, because at this point they also had to notify you/have you opt in into sharing your data.
That's the definition of opt-in
A couple years ago this came up and someone made this claim, but no one could ever name an OS where this is the case. Maybe someone on HN knows one? :)
https://github.com/mobile-shell/mosh/issues/156#issue-407789...
"Note that denying other users read permission [to your .profile] does not mean that they cannot find your PATH or any other of your environment variables. The -eaxww options to the ps command display in wide format the environment variables for all processes on the system"
Ultrix 2.2 was a BSD 4.2 variant by DEC. I doubt it was unique in this behavior, my guess is all BSDs leaked info in this way, but I don't have a reference handy. mkj's example in this thread of AIX suggests Sys V did it too. Modern Linux only shows you your own processes' environment variables.
(Young people of Hacker News: beware what operating systems you learn because 31 years later their idiosyncracies will still be burned into your brain.)
https://github.com/sorah/envchain
Not really something you would use for production web apps, I think envconsul covers that usecase:
Without considering resources or practicality, if we were to re-design computers and servers from security-first principles, what would features like management of secrets look like? Secure enclaves are wonderful but the secret still has to be propagated or used. A ground up computer design might greatly embellish on the idea of a secure enclave.
Linux seems a bit of a dinosaur in this regard.
Imagine your enclave is a separate server on the network: how do you define which processes get access to which secrets under which circumstances? How do processes prove who they are to the enclave?
To be fair, it has only whatever is on-hand to use. And the burden of running in a lot of different environments. Apple, having control of the hardware, and a very specific/limited set of places to run, can do smarter things around enclaves, etc.
edit: Apparently systemd now has an option to pass secrets/credentials to a service through a more secure by default (i.e. only stored in memory) file option: https://www.freedesktop.org/software/systemd/man/systemd.exe...
In general, it's better to design subclasses to be constrained versions of the parent. If you want to add functionality to a class you're better off using another technique like composition, or even just a simple utility method.
Another way to state the issue here is that a user of an encrypted string will always need to know that it is an encrypted string because the only thing cyphertext is useful for is feeding the decryption algorithm.
I assume by certs and Windows auth they're implying the OS native stuff versus just passing those around in code, instead
I feel like this is an antipattern in OOP. Shouldn’t the functions just be defined to take in parameters of type SecureString instead?
I think this violates the liskov substitution principle (https://en.m.wikipedia.org/wiki/Liskov_substitution_principl...). For example, how is the “length” function or “startsWith” defined on SecureString? It seems like either data would be leaked via side channels, or the behavior doesn’t really conform to the String specification.
I think this is a good point, and perhaps worth considering, but in most of my thought exercises, it's worth it, owing to the fact that when I use SecureString in other languages, I find myself having to decrypt the string more often than I would like. I guess one could argue that this is a deficiency of the libraries and such that are used, rather than a deficiency of the fact that SecureString doesn't extend String. I'm having trouble recalling a specific example right now, but I know I've run into it before, because that's what prompted me to implement this many years ago.
That’s a problem because you are enforcing all your functions accepting normal strings to either have to check if it’s NOT a SecretString or to return some nonsense. For exemple a function that returns the 2 firsts characters without caring about secret string could be passed one as argument and return « *s ».
The Liskov Substitution Principle states that a function accepting an Animal (a String) should never care about wether it’s a Cat (a Secret) and don’t fear Animal (String) methods to return meaningful values.
If you're using containers, only other users in your container will be able to see leaked secrets. Typically there's only one user in a running container. If an application in the container gets hacked, the whole thing is vulnerable anyway, leaks or not. Or if the container was running in privileged mode, which compromises host security. If someone gets access to your Docker host, or root access on the Docker host (privilege escalation to root is pretty trivial on Linux) then they can see all information, leaked or not.
If you aren't using containers, and just a regular Linux OS, a couple methods exist to harden process information between users (such as cmdline and environ) to contain most of the leaks.
What you do want to avoid is writing secrets to persistent storage, or filesystems that many different containers or users can access. You also want to prevent passing secrets to containers as environment variables, as your orchestration system might expose them to more users accidentally, and some logging systems might log them.
The best practice for passing secrets into a container is to have your container orchestration system pass credentials to the container, such as with an instance metadata service, or temporary volume-mounted secrets filesystems. The container would use that passed credential to then access a secrets manager.
The only annoying thing about `pass` is the generic name, so searching for information on it, or dealing with issues is a pita. Luckily, there are few.
Edit: I was wrong about what this article covers. Which is how to pass secrets to processes without leaking to `ps` or audit log.
----
Pass is still worth a look, https://www.passwordstore.org/
Keeping the password storage in a gitlab repo makes it very useful for managing those secrets internally. Keep the list of public gpg keys for each team member, and a README, some helping initialization script for setting it up, and that's pretty much it.
Then, all cases presented where the command would either be used directly in the command line, or within another script, is just replaced with a call to
$(pass the/secret/we/need)A few comments on the actual article, which was a lot more insightful than I had thought:
- Protecting yourself from an audit logs is not really worth the effort, as the audit log should be treated as confidential as the secrets themselves.
- Utilities can themselves hide arguments, and will not show on the ps output. For example, `mysqlsh` shows as `--password=********`, as does `mysql`. I'm not 100% if modifying argv data has any effect on /proc, but then again, if you are protecting yourself from something that can access /proc (i.e. root), then nothing will work in the end. Protecting yourself from a rogue process that can monitor `ps` is also already a lost battle.
on Linux and all other Unix-like systems I know of, all users are allowed to list running processes and their command lines.
> Protecting yourself from a rogue process that can monitor `ps` is also already a lost battle.
ps is not a privileged command on most systems. POSIX says "On some implementations, especially multi-level secure systems, ps may be severely restricted and produce information only about child processes owned by the user."; "severely restricted" implies that this is not a common behavior.
furthermore, the article explains clearly how to hide secret arguments from ps. that's the main point of the article.
after searching for "PUT" in the manual, I found "-T, --upload-file <file> [...] If this is used on an HTTP(S) server, the PUT command will be used. Use the file name "-" (a single dash) to use stdin instead of a given file."
Edit: Here’s a link https://github.com/lastpass/lastpass-cli
But yeah, reading secrets of env or ps or the clipboard is a real issue, so I focus on making sure that doesn't leak.
I've made terrible mistakes leading /proc accidentally in my Web app https://github.com/securego/gosec/issues/569
In actuality, the Doppler CLI (a Go binary) fetches your secrets from Doppler's API and injects them as environment variables into your specified process. That looks something like `doppler run -- printenv`. This prevents your secrets from being written to the filesystem in plain text, and prevents the environment variables from being available more broadly. In the case of docker, you would bake the Doppler CLI into your image, thereby sidestepping the documented `docker inspect` pitfall.
Of course, the CLI still needs a way of authenticating to Doppler's API. You authenticate and authorize the CLI by running `doppler login`. This initiates a login flow that you complete in your browser. Once completed, your newly generated auth token is sent back to the CLI. The CLI then saves the auth token to your system keyring for later use. The identifier needed to access that keyring entry is then stored in plain text in the CLI's config file (~/.doppler/.doppler.yaml), which is only readable by the user.
We're exploring other means of injecting your secrets into your application, as some users are wary of using environment variables. This is a challenging problem though as there are few means of injecting secrets that don't require substantially changing your application's logic.
Given enough time and enough secrets, it becomes asymptotically likely that a secret will become exposed. Ensuring that those secrets expire quickly is a good way to mitigate this.
...and then the variable containing the secret is expanded in a non-builtin simple command in the following snippet. It is taking a gun, pointing it at your leg, and pulling the trigger, very deliberately, if you know how POSIX shells work.
There's new hidepid mount option on Linux though.
Sorry for the top level comment, but I am surprised this hasn't been shared by someone else already. Process substitution(1) (e.g. <(SECRET_STUFF_HERE)) totally solves the problem of leaking secrets to the process table.
From RTFA, the author is using "$(< $STEPPATH/certs/root_ca.crt)" and is concerned about leaking "$STEPPATH/certs/root_ca.crt" to the process table. If the Author instead ran <(< $STEPPATH/certs/root_ca.crt) the process table would just have some junk ephemeral file path instead.
For example... lets say for an arbitrary example, I didn't want folks knowing from the process table that I was getting the file details of /etc/passwd
### LEAKY
~ % wc /etc/passwd
110 297 6946 /etc/passwd
I could instead use process substitution: % wc <( < /etc/passwd )
110 297 6946 /dev/fd/11
I have found this to be most useful not for files, but for temporary secret variables that I want to wrap in a file, but not have to deal with the cleanup and management of that file.For a shell variable example, rather than writing "B64 is not encryption" to a file, and then having to cleanup that file... use process substitution to create the temporary file which doesn't leak to the process table.
## Don't do :
~ % echo "B64 is not encryption" | base64
QjY0IGlzIG5vdCBlbmNyeXB0aW9uCg==
## Do this instead; also 'echo' is a bash builtin and won't leak
~ % base64 <(echo "B64 is not encryption")
QjY0IGlzIG5vdCBlbmNyeXB0aW9uCg==
Or if used in scripting: secret="B64 is not encryption"
bar="$(base64 <(echo "${secret}"))"
Some caveats: This only works for commands that accept files for arguments. If a command requires a secret to be passed in as a plaintext argument, then maybe the right answer is to rethink using that command in the first place (maybe <<<"HERESTRINGS" (2) helps in that case though I doubt it).PROTIP: And for the love of all that's holy... run shellcheck(3) before considering running your script for realz if you want to keep your butt out of the fire. Also, the google shell style guide (4) is full of practical/good stuff. Shellcheck and the google style guide are pretty magical. I lost 10 lbs without dieting, became more attractive, and married my wife from following the guidance from those two resources. You can too! (caveat lector, ymmv, not legal advice (ianal), wife is already taken... sorry)
--- (1) https://www.gnu.org/software/bash/manual/html_node/Process-S... (2) https://en.wikipedia.org/wiki/Here_document#Here_strings (3) https://github.com/koalaman/shellcheck (4) https://google.github.io/styleguide/shellguide.html
On macOS, you can use pbcopy and pbpaste from/to the clipboard. On Linux, that's:
alias pbcopy='xclip -selection clipboard'
alias pbpaste='xclip -selection clipboard -o'
Or use secret files or something like Vault for automation.So the workflow usually looks like following:
$ secrets aws-credentials # prompts a GPG passphrase
$ aws s3 sync ...
[0] https://github.com/chuwy/zsh-secrets env $(pass env/$1) ${@:2}