tl;dr to avoid GC copying secrets around, they just mmap off the go heap.
I made secstr for Rust a while ago. Didn’t have to do anything like that thanks to Rust’s no-GC memory mangement :)
If you use crypto implementations in the standard library you are pretty much back to square one, as they copy key material. https://github.com/golang/go/issues/21865
I don’t fully understand the threat model. Presumably unprivileged processes cannot read another users processes arbitrarily, and if they can, then isn’t it just making the attack slightly harder? Do kernels not wipe physical memory when reallocating it to other processes?
What does this defend against?
I think people take it a bit too far, but the various threats include:
Do a crypto op. Do an insecure op. An exploit finds the key leftover from previous crypto op.
A variation of sorts of the above, the memory gets reused, leaks, oops.
There’s some flavor of kernel bug that leaks memory, and you’d like to narrow the window of vulnerability.
Suspend, hibernate, cold boot, etc.
I think the threat model is basically adversary gets a snapshot of memory at some point, so you’d like it to be uninteresting.
Also: process crashes, core dump gets written with secrets inside of it, adversary gets access to disk.
Suspend sounds like a good use case. I might have to patch some go code I have.
Maybe add “code ran in VM that moved” to 4 depending on whether whatever manages them overwrites memory before dropping a new one. This could become a bigger risk for platforms that use lightweight VM’s/containers with rapid launch and shutdown.
It is, indeed, a lot of complexity for a threat model which most don’t need to worry about: naive RAM scrapers which they’d like to trip with the guard pages. A sophisticated attacker with remote code execution can easily sidestep such a defense, because the secrets are still sitting unencrypted in RAM.
A better approach would be to actually encrypt the sensitive information. This is particularly useful for cryptographic keys, because we can e.g. use a key-encrypting-key sitting in XMM registers to decrypt an encrypted data-encrypting-key (DEK) into other xmm registers. That is to say, we can keep an encrypted copy of the DEK in memory, decrypt it into xmm registers when we want to use it, perform cryptographic operations with it, and then we never have to worry about the unencrypted version sitting around in memory in the first place.
Or, if you’re really worried, use something like Intel SGX, or perform encryption using a separate physical device like a TPM, HSM, or Yubikey
I agree that encrypting secrets is the next logical step. I’ve been planning a scheme for a while now and it should hopefully land in the next major update, time permitting.