Lately, I've been thinking a lot about a concept that Nathan McCauley and I came up with a few years ago: crypto-anchoring—and how much impact this kind of architectural decision could have in the breaches that we've been experiencing lately.
It turns out that the vast majority of data breaches follow a pattern like this:
- An attacker hacks into company X's infrastructure.
- The attacker exfiltrates sensitive content (hashed passwords, etc.).
- The attacker has fun with the data at home (password cracking, etc.).
And even though there are thousands of different security products focused on detecting each step of the attacker killchain, it's time that we start architecting our applications—and data-flows—in a way that makes it harder for attackers to continue following the same script.
Take the simplified example of a data-exfiltration attack depicted below:
In this particular example, an attacker accesses and exfiltrates the contents of the user database containing all of the user information, including the password hashes used for authentication to the service. The attacker is now free to crack these passwords anywhere, and if the attack and exfiltration of the database aren't immediately detected, you might never know what happened until the cracked passwords start being sold on the black market.
Slowing attackers down
What if we could architect our systems such that the attacker can't use the stolen data outside of our infrastructure? Doing that would give us the following advantages:
- More chances for detection of the attackers, since they have to operate within our environment.
- Logs that show precisely what pieces of data have been accessed, allowing us to assess the impact an attack more accurately.
- Force the attacker work in an adversarial environment, slowing down their progression by rate-limiting services that allow access to sensitive data.
By forcing the attacker to have to operate within our infrastructure, we are making them operate like a bull in a china-shop.
Keeping attackers in
Let's look again at the example of our canonical database leak. If we want to force an attacker not to be able to crack a password offline, we have to make the computation of the password hash dependent on something that can't leave the data center. Maybe a key generated inside of a piece of hardware physically bolted onto your servers?
It turns out those already exist, and are called HSMs (Hardware Security Modules). You can buy your own HSMs if you’re running your own datacenter, or use the ones available in various cloud providers.
Let's take the same attack as before, but instead of simply hashing the password (
H(password)) we first apply an operation using a key generated inside of our HSM (
HMAC(key, password). Adding this extra step makes the contents of the database the attacker exfiltrates contain hashes of passwords that can't be re-computed without the ability to query the HSM.
That brings us to the concept of a crypto-anchor:
A Crypto-anchor is a service that forces a data-flow to only be available within the boundaries of your infrastructure.
Let's take a look at a couple more examples of using crypto-anchors:
Data-flow: You have a credit-card processing service that needs the ability to temporarily persist and later decrypt end-to-end encrypted credit-card data coming from a remote hardware device.
The most straightforward way of implementing this data-flow is by giving the payments service access to the private-key that decrypts the transaction information coming from the remote credit-card reader. This has the obvious downside that an attacker that compromises the payments service can exfiltrate both the encrypted transactions and the private-key that is necessary to decrypt them.
If instead we crypto-anchor this data-flow, we disallow the attacker from decrypting any of these transactions outside of our infrastructure.
The easiest way of doing this is to have the private key necessary for decryption stored inside an HSM, exposed behind a decryption service with strict per-service rate-limiting on the number of allowed decryptions.
Data-flow: You need a tokenization system that creates a stable identifier corresponding to a specific piece of sensitive data, say a user's Social Security Number1.
The obvious way to implement a tokenization service is to generate a random token and store a mapping of that token and a one-way hash of the sensitive piece of data.
Unfortunately, the maximum number of possible SSNs is just under 1 billion, making it trivial for an attacker that downloads the database to brute-force them offline.
Similarly to the previous example, if you wanted to ensure the attacker couldn't brute-force the SSNs offline, you could add a crypto-anchor in the data-flow, turning the offline brute-force attack into an online attack.
The easiest way to accomplish this is to have the token generation process be a keyed one-way function, that can only happen inside of an HSM.
If we generalize the examples above, we can easily see that when we're architecting a system that deals with sensitive data, we should make sure to:
- Never expose a service that stores sensitive data directly to any internet-exposed (front-end) services.
- Split core functionality into independent services, allowing for natural crypto-anchoring points in your data flow
- Categorize services with different levels of security into different security zones, and rate-limit the number of API calls to the crypto-anchor on a per-zone or per-service basis.
By designing your applications in a way that ensures sensitive data-flows are crypto-anchored to your data center, you are:
- Slowing attackers down.
- Gathering better information on what data was exposed.
- Making attackers continuously risk detection by forcing them to operate on your turf.
So go out there and anchor your data! ⚓️
Equifax anyone? ↩