DEV Community

Cover image for Encryption as a Service in Action
Sergio Díaz
Sergio Díaz

Posted on

Encryption as a Service in Action

TL;DR

After reading this blog post you will have an overview of why we need encryption as a service. Also, we will implement a PoC using Python and Hashicorp Vault to apply what we just learned.

Overview

Nowadays there are companies that deal with sensitive data such as banking or health information. This means that in the case of a security breach, the company could have legal, financial, and bad PR repercussions. As a result, there are some industry standard guidelines for storing this type of data (eg. Payment Card Industry Data Security Standard). For example, when it comes down to store Credit Card Data, organizations are required to encrypt the account numbers stored in databases.

Encryption in transit

While encryption at rest can be seamlessly be done by using a cloud provider, handling the data when it’s moving around services can be tricky. Yes, you can use a secure communication channel (eg. TLS) between services, but this is not enough. One thing that could go wrong, even when using TLS, is logging sensitive information by mistake. This can happen even to companies that are worth billions of dollars and with an awesome engineering team.

Handling encryption at the application level

This is possible since there are good cryptographic libraries out there. However, developers should do the implementation in a correct way (eg. not using deprecated ciphers).

Furthermore, while a company might be running a Majestic Monolith, chances are that you have other services that interact with your main service. This leads to questions such as:

  • What happens if more than one service needs access to the sensitive data?
  • Which service will handle encryption and/or decryption?
  • How will encryption keys be handled, rotated, and distributed?
  • What happens if a key is leaked?

Using a Key Management System

The solution is to protect sensitive data with a centralized key management system. This can be done using Hashicorp Vault, AWS KMS, Google CMK, etc. The idea is to delegate the responsibility of encryption and decryption to this service. Using a solution like Hashicorp Vault, allows you to encrypt and decrypt application data with an HTTPS API call. This means that:

  • Data can be encrypted at rest
  • Data is secured in Transit (TLS)
  • Key handling and cryptographic implementations is taken care by Vault, not by developers
  • More services could be added to interact with the sensitive data

How it works?

diagram

You can find the source code here.

As you can see we have 3 services:

API

This application allows you to create a "credit card" with a name and a PAN (primary account number). All data is stored encrypted and API doesn't know anything about the type encryption used or how to decrypt it.

Card Processor

This service process the credit cards we have created so far according to the business logic. Just as with the API, it doesn't know anything about encryption or decryption.

Vault

This service handles the encryption done by the API and the decryption done by the Card Processor Service. If a new service is added in the future, everything regarding encryption or decryption would still depend 100% on Vault. The best part is that no changes would be made in the other services.

How to Run

Just do docker-compose up --build and you should be ready to go.

Configuration

Run the following script:

./bin/vault.sh
Enter fullscreen mode Exit fullscreen mode

3 things just happened:

  1. The transit Secrets Engine was enabled. This tells vault to not save data and turn on the encryption as a service functionality.
  2. The root token was created. This one is used by our apps to encrypt and decrypt.
  3. A symmetric key was generated. This one is used to encrypt and decrypt the respective data.

Grab the token from the output value and put it as VAULT_TOKEN env var in:

  • app/settings.py
  • card_processor.py

Using the credit card API

Send a POST request to http://localhost:8000/credit-cards/ with the following body:

{
    "name":"Carlos Gardel",
    "pan":"4539296620131157"
}
Enter fullscreen mode Exit fullscreen mode

If everything went well you should receive the the encrypted Primary Account Number (pan).

{
    "name": "Carlos Gardel",
    "pan": "vault:v1:JnC8pS/zmHhHPGd7dk5eCGilnUi8odvRIBP9Z+rBmMLAWXJ/dgYqGAU4MTk="
}
Enter fullscreen mode Exit fullscreen mode

If you do a GET request to http://localhost:8000/credit-cards/ you will still see the encrypted PAN field.

Using the card processor service

Now let's processes the card using the card_processor.py service.

python3 card_processor.py
Enter fullscreen mode Exit fullscreen mode

We should be able to see that the card was successfully decrypted and processed:

Processing CARLOS GARDEL card with number 4539296620131157
Enter fullscreen mode Exit fullscreen mode

Rotating Keys

To minimize the risk if a key is leaked, let's tell Vault to rotate it.

./bin/rotate.sh
Enter fullscreen mode Exit fullscreen mode

If we do another POST request to the credit card application, we will see that we used v2 to perform the encryption.

{
    "name": "Aníbal Troilo",
    "pan": "vault:v2:vdIlXggLzrM4n5Xlzxh6a/xpmd7yz/F9MsoifuR/kmOodGKV5wPaWvMMiEw="
}
Enter fullscreen mode Exit fullscreen mode

Let's do a GET and see our different encryption key version (v1 and v2):

[
    {
        "name": "Carlos Gardel",
        "pan": "vault:v1:JnC8pS/zmHhHPGd7dk5eCGilnUi8odvRIBP9Z+rBmMLAWXJ/dgYqGAU4MTk="
    },
    {
        "name": "Aníbal Troilo",
        "pan": "vault:v2:vdIlXggLzrM4n5Xlzxh6a/xpmd7yz/F9MsoifuR/kmOodGKV5wPaWvMMiEw="
    }
]
Enter fullscreen mode Exit fullscreen mode

Let's go back to the card_processor.py service. Now we are able to decrypt and process both entries. One was decrypted with v1 and the other one with v2.

Processing CARLOS GARDEL card with number 4539296620131157
Processing ANÍBAL TROILO card with number 2720997130887021
Enter fullscreen mode Exit fullscreen mode

Voilà. Encryption keys and rotation were handled by Vault.

Further considerations

Are we production ready yet?

I don't think so.

  • We are using a root token
  • TLS and communication between Vault and services is yet to be implemented
  • Vault needs to be sealed
  • We need to make sure that the Vault service is reliable (did someone say K8s?)

Open Source vs Cloud Provider Solution

Do you want to deal with a self-hosted open source solution or would you rather deal with your respective cloud provider’s solution?

Wrapping It Up

If you want to discuss or challenge the implementation I’m happy to do so. Just leave a comment or find me on twitter.

References:

Top comments (0)