An Obfuscated String Implementation for Go


Recently, I purchased the domain rit.singles, and I was possessed with the urge of making an actual dating website. I thought it would be cool to write it in Go, since that's one of the hip new languages of the now, and also because it does a lot of things that I really like.

I started by writing the account management and registration code. This is surprisingly tricky, because handling sensitive data with any semblance of security is a pain. Luckily, Go exposes a number of very useful syscalls for us to work with.

Since I'm not offering enough click-bait on my block, I feel like it's only appropriate to turn the rest of this post into a listicle.

4 Weird Tricks One Florida Man Used to Protect Sensitive Data in Memory!!! (Hackers hate him!)

1. mlock your pages

Allocate as many pages of memory as you need to hold your string. Then mlock them immediately. mlock is a portable function that strongly encourages your kernel not to swap out a page of memory.

Why is this useful?

Swap doesn't get zeroed out, and there's no way to guarantee when it will get overwritten, so you don't know how long any persisted data will last. While encrypting your swap is a good idea, you can't expect everyone to encrypt their swap paritions.

Any caveats?

Hibernating will still persist passwords to swap. Sorry.

Solaris and Solaris-based operating systems require that you grant the proc_lock_memory privilege before a user can run anything that calls mlock.

Memory that you mlock must be page-aligned. I mmap my sensitive memory to ensure this.

2. mprotect your pages

mprotect assigns permissions to your pages. I typically write my sensitive data, and then immediately mprotect the page such that it's read-only. If any process tries to write or execute that memory, that triggers a segfault, which is much better than tampering with sensitive memory.

Why is this useful?

Instead of a rogue buffer overrun wiping your sensitive data, you instead trigger a segfault. This helps prevent tampering, either malicious or accidental, of your sensitive data.

Any caveats?

Again, the memory that you pass into mprotect must be page-aligned.

3. Encrypt your sensitive strings

It's really just obfuscation rather than offering real security, but it's a worthwhile tactic. Passwords become less trivially identifiable if they're encrypted in memory.

Why is this important?

If an attacker gets a memory dump, or if a machine hibernated and an attacker has your swap device, it makes it far harder to identify a password.

Any caveats?

Hell yes.

The key needs to be stored in memory. If the attacker knows where in memory your key is, then it's trivial to decrypt your sensitive string. It really just ends up being obfuscation more than anything.

4. memset_s your pages before you munmap them

memset_s is a new C11 function that will memset with the guarantee that it won't be optimized out.

If the value that you memset isn't used after the memset, smart compilers will optimize it away. If you're clearing sensitive memory after you're done using it, this is definitely not what you want.

Why is this important?

When you free or munmap memory, it isn't wiped before being given back to the OS. If an attacker gets a memory dump after you have freed a sensitive block of information, it's very possible that the value is still in memory.

memset isn't sufficient for the reason I outlined above.

Any caveats?

memset_s isn't implemented in many libc's. If this is a case for your targets, then check out this implementation from CERT.

Now where the hell does Go come into all of this?

Since I'm working with passwords for rit.singles in Go, I felt the need to write a secure password implementation in Go. In it, I did all of these things, save for clearing my data with memset_s. As far as I'm aware, Go's compiler won't optimize away a normal memset, so I'm safe there.

The code for the secstring package is on GitHub. The documentation is on GoDoc.

I welcome any bugs/criticisms/questions/audits.