Encryption TechniquesAt a high level, you can think of two kinds of data encryption inside of MySQL or any similar data store. I’ll oversimplify for purposes of illustration. You can:
- Store the data in MySQL as normal, but encrypt the container holding MySQL. Usually this means storing MySQL’s data on an encrypted disk volume. The protection? Broadly speaking, if someone gains access to a backup disk, they can’t see your data.
- Encrypt the data before sending it to MySQL. In this case, the security boundary is pushed out further: even if someone gets access to the server, and can run SQL commands, they can’t see your data.
- Will data be exposed if backups are unencrypted?
- Are sensitive values possibly in cleartext in query logs?
- Will sensitive values be visible in status commands like SHOW FULL PROCESSLIST?
Making It ConvenientConvenience is important. If it’s too hard to do encryption, there’s an increased risk that it won’t be done. Fortunately, Go’s elegant interfaces for the
database/sqlpackage make the burden transparent to the programmer. We learned how to do this from Jason Moiron’s excellent blog post on the Valuer and Scanner interfaces. Please read that if you haven’t yet. To implement transparent encryption and decryption, we created a custom data type to implement the Valuer and Scanner interfaces. The implementation is straightforward and quite similar to Jason’s example of compressing and decompressing, except we used encryption libraries instead. Now our code is incredibly simple to use with encrypted values. All we do is define a variable of our custom type. For example, instead of
We simply use
var password string err = rows.Scan(&password)
It’s similarly simple to insert values encrypted into the database. Magic! This is why I often say Go’s design, although it seems minimalistic at first, is actually very advanced and powerful.
var password EncryptedValue err = rows.Scan(&password)
Nuts And BoltsThe code is small. The exact details of all the code aren't important for this blog post; much of it is about things that are out of scope here. The gist of it, though, is we store values as byte arrays:
- The first byte is an indicator of the version of our encryption algorithm used, so there’s a clear migration path for changes.
- The next four bytes indicate which key we used to encrypt this value, so we have 4 billion possible keys.
- The rest is the encrypted payload.
switchon the first byte’s value, if we want, to determine whether the key ID is in the next 4 bytes, or if it’s something more, such as the next 8 bytes. So we can easily expand the number of keys we can indicate. We can also, if we ever hit version 255, use that to indicate that the version number continues in the next byte. This is a standard trick used, among other places, by the MySQL wire protocol. The result is that we have a simple and future-proof way to encrypt values.
Alternative ApproachesIn addition to the approaches we’ve mentioned, there are several others. There are commercial projects designed to help ease the encryption and decryption techniques you might otherwise wrap around MySQL and perhaps fumble in some ways. There are encryption functions inside of MySQL—but educate yourself about those before using them. There are others, too, but you should be able to find all you need with a search.
ConclusionsBy using Go’s built-in interfaces, we created a solution for transparently encrypting values in our database so it’s never in the database in cleartext, either on-disk or in-memory. The code is easy for programmers to use, which improves our security posture automatically. All sensitive data gets encrypted in-flight and at-rest, and an attacker would have to have extensive access to our systems (an SQL injection wouldn’t suffice) to be able to decrypt the data. We highly recommend you use the standard Go interfaces for the power they give you. And please, ask your SaaS providers hard questions about security and how it’s implemented. Every service needs to be secure to make the internet a safer place.
Post Updated 7/31/2017