Closed tcheneau closed 6 years ago
Thank you very much for reporting this.
This might be a dangerous default indeed. The problem we're facing here is that we want to make gopass accessible to non-techie users while still keeping it as secure as possible. Not using always trust can protect you against attacks like this, but also makes it very tedious (i.e. almost impossible) to use for beginners.
We could keep alwaystrust enabled but avoid importing any key material, but then the users would have to import keys manually. Something that has also proven to be a show-stopper for many of our beta testers.
Right now I'm not sure how to move forward with this. Any input/ideas would be appreciated.
I understand your will to make the solution easily accessible to the beginners (that's one aspect that made it compelling for us as well), but given the sensitivity of what is stored in a repository, it shouldn't come at the cost of a decreased security.
I'm not really sure on what would be best, mostly because I haven't ready gopass' code and because I'm not too familiar with PGP's trust model. My understanding is that we should never call gpg with "alwaystrust (ever)", so has to make sure this category of attack will never work. To my mind, the following workflow would be ideal:
What do you think?
This sounds like a sensible approach, thank you for that.
Let me give this some thought and if we can't think of anything that's wrong with that, we'll try to implement it.
Thank you. Please let me know when you have some solution/code you want me to try out.
Hey @tcheneau - I kind of like your approach, but it still doesn't cover all the bases. One fundamental flaw with the current security model (inherited from pass) that I still see is that the list of keys "to encrypt for" is a public, unsecured part of the respository.
Even if we implemented your suggestions, the following attack scenario is still uncovered:
Obviously, this isn't very stealthy as it's clear who's siphoning off the secrets, but it's nevertheless real and currently undetected until it may be too late.
IMHO, the fundamental flaw here is what I described above - the security model relying on an unsecured and public file. The only current way to limit exposure would be limiting access to the repository.
My idea for remediation would be the following:
root token
is created and encrypted in the root of the repository. This root token (initialized as a random, 128 char hex string) is from here on decentralized proof of ability to decrypt the repository that is neccessary for the next steps.The nicest part about this approach would be a complete cryptographic protection of the owner management process, as well as nice and convenient UX. It would build a base for some future features such as fine-grained owner management or request/grant schemes. From a filesystem perspective, it still fits well with the git based approach and should be easy to auto-merge.
But obviously, this approach has a few heavy drawbacks as well:
pass
Thoughts and comments very much appreciated.
@tcheneau After giving your proposal some more thought I was about to raise the issue of the unprotected .gpg-id file, but I think @falschparker82 provided a much better explanation than I could have given.
@falschparker82 So far I can't spot any issues with your proposal and really like it. I'd definitely go for subfolders to structure the additional files, but that's a minor implementation detail.
IMHO the most reassuring fact about your approach is that it doesn't weaken the current level of security in any way and it doesn't break compatibility with any other tools based on the password store layout of pass
. Also it's backward compatible!
We may need to think about non-interactive use (i.e. what to do when piping to gopass insert
and encountering a non-trusted key ID), but that affects other operations as well.
(EDIT: retracted / deprecated proposal below)
Actually, I thought some more about this - there's another way to keep most of these properties and make it considerably simpler (where simpler usually means better in a security context): We could fuse both properties of authenticating new owners and proof-of-ownership into a single step.
So here's "Approach B":
That means getting rid of the token proposal and all the signing steps from the older proposal and just encrypting the .gpg-id file with the current owners - as well as keeping the public one. This enables cross checking both public and encrypted .gpg-id file when encrypting secrets. That way, nobody can alter this file in any undetected way without being able to access the decrypted secrets. The workflow is also simpler and would just mean altering both public and encrypted versions of the .gpg-id file when adding owners. Trust would be implicit that way as in you would trust owner changes by owners (more on that below).
Advantages to approach A):
Drawbacks to approach A):
GRANT
privileges one way or another, as by being able to decrypt any secret offline, they can effectively use, abuse and share them however they feel. So approval of a key is pretty useless in the first place if another owner has granted access already.Ideas and comments welcome.
Isn't "Approach B" easy to attack? If Mallory can assume the content of the encrypted .gpg-id.enc
matches the content of the plain text .gpg-id
he can just add his key to .gpg-id
and save encrypt(.gpg-id)
to .gpg-id.enc
. With asymetric crypto this is intetionally possible without possession of the private keys.
This would still leave a trace in the git history, but no cryptographic trace.
IMHO we can't protect the recipient integrity without some signature including a secret only known to existing, valid recipients as you outlined in "Approach A".
Yes, you're right, guess I was too hyped about finding a simpler approach and quick on the finger.
The part where I totally slipped was that ability to decrypt != ability to encrypt
.
Approach A had quite a lot more germination on top of my head for a few more months already, I think it's structurally more sound. Forget the second one.
Hello, I gave it a few days to think about the root token proposal. The idea is very compelling.
I have a few comments:
Hi, this is very good input. I'll try to answer as good as I can from my side.
gpg
may be too annoying. Decrypting the root token once and checking an small (N < 1000) list of HMAC signature should be not a problemsomeone adding a previously removed key to a store again
? In that case this makes sense to add a counter (and probably also a timestamp, see me first point in this comment)Hi guys,
it's good there's some pondering going on, there's also one more attack vector I completely overlooked (thankfully, considering it was triggered by discarding approach B): IF we assume full control by Mallory over the repository filesystem (which we do in this case), that includes mutations and deletions as well. As the encryption is asymmetric, the root token can not be read by Mallory but it can be reset, so she could simply set a new root token, correctly sign it and delete Bob's signature. If Bob now pulls the repository, he will get a warning because his signature can't be verified, but as this is incidentally the same case as being granted ownership of an already existing repository, he'll get the message that Mallory already vouched for trust. If Mallory now also disguises as someone else (and the fingerprint is not checked), this could lead to a breach. As said, crypto is hard.
I've thought a few times about this, and if the identity of Mallory's key is shown (and the fingerprint is compared), it should be enough to detect this attempt. Which means that the design is still solid if the own signature is always verified before encrpytion and the full GPG information of any trusting other user is displayed with a big fat warning of this intrusion possibility when co-signing an existing repository.
However, it's possible this is overlooked - and the attack vectors to be considered with full file system access are vast. The only remedy I could think of was always some form of "pinning", i.e. saving a hash of the root token in your local configuration.
Maybe you guys have some other thoughts about this.
Chiming in to your thoughts as well:
This is a really good read after coming back from vacation. Has anyone done research on how other password managers are dealing with that problem? Is it really specific to gopass? Before going full steam ahead on implementing we should maybe look at what others do too? :thinking:
Most password manager I'm familiar with either don't support this workflow (like KeePass, pass and some others) or use a centralized trust model (basically all the commercial "cloud" password stores).
Keybase is doing rather interesting things by creating a "chain of trust" similar to some of the suggestions. Every single change is "signed off" by a device key and they are using a similar idea to blockchain where all future transactions are built on top of previous ones, so you can trace back the whole history. If you stored your repository locally and had it synced somewhere, you could use another computer to "verify" that the chain is complete and hasn't been tampered with.
As a first step, can we imagine implementing a simpler (although less secure option as @falschparker82 mentioned in one of the first comments) where we ask the user upon addition of a new key to a repository if this new person is to be trusted ? This idea is to fix the subject of this issue quickly, and implement a more complete solution after. Maybe this behaviour could be bypassed with a config key.
Yes, this makes attacks where user already trusted on another repo can gain access to the current repository, but:
--home-dir
in gpg for each gopass repo). Although we might have to copy the current users key to those gpg-store (and maybe some other things).Also, as a side note, the gpg-store, and the associated keys and trust is used by other programs (say for validating software packages). Changing this may expose the user to other attack vectors (an attacker adding it's key to the gopass repo may be able to deploy correctly signed malware to the target machine). This argument pushes toward use of separate gpg-store for gopass repositories.
My thoughts on this issue is that gopass should not manipulate the user's store implicitly, especially if it is changing trust settings (especially on auto imported keys).
This should be mitigated by #800. It's still not the perfect solution, but at least this makes it very hard to not notice manipulated recipient lists.
Hello,
The alwaystrust config option has been removed in issue #206 and now the alwaystrust argument is always passed to gpg. If the autoimport feature is enabled (default true), I see a huge security issue.
If an attacker manages to commit to my repository a GPG key I do not trust, and I'm not careful when pulling (can easily be overlooked if there is a lot of changes in the history), next time I work with the repository, I will begin reencoding my passwords with the attacker's public key. My understanding is that if alwaystrust was not set, the pubkey of the attacker would still be added to my keyring, but gopass would not reencrypt my password file with its pubkey (it would probably spit out an error message).
I actually was confronted to this behaviour this very afternoon when trying to set up the tool with a colleague of mine and it certainly does not look like a sane default behaviour. Am I missing something obvious here?
Regards, Tony