English Portuguese

Building Data Breach Resistant Applications: Bring Your Own Database

Build applications and protect them against cyber attacks is a tough challenge. What if we could build an application that even if breached won't expose customer data? There are solutions for this using end-to-end encryption, which is great, although they're not open source or widespread, but I just thought about another model: bring your own database.

Before diving into cryptographic solutions to build data breach resistant applications, I began to think in alternatives and one of them was to let the users store their own data. It opens up another way to think about applications in general with some advantages and drawbacks that will be discussed further in this post.

For the sake of this post I've built a proof of concept (one file application) using Sinatra, a very simple ruby framework. Let's call this application "Bring Your Own Database (BYOD) Application" - or BYODA in short.

Here's how it works:

  1. User signs up and register his/her database address and credentials to BYODA
  2. BYODA creates tables on User's Database Management Systems (DMS)
  3. BYODA perform any operations and read/store in the User's DMS

Requirements for this model to work

Challenges

  1. It's hard/costly/burdensome for users to setup their DMSes
  2. Applications don't have built-in Object Relation Mappers (ORMs) to handle data read/persistence across multiple DMSes
  3. There is an inherently performance penalty using high-latency databases or handling multiple database connections with multiple threads
  4. Users don't know to protect their data, even if they have it under their control
  5. In case of closed source code, there is no guarantee that the application itself won't make a copy of records before/after transmitting them to/from User's DMS

Solutions

  1. Offer a no-brainer service to setup a DMS
  2. Develop a custom code to handle data read/persistence across multiple DMSes or await a kind soul to do it for each language/framework. It's not trivial to do it correctly because there is a need to manage connection threads as well
  3. The (1) solution must setup the DMS NEAR or IN THE SAME data center as the BYODA
  4. Opportunity for a new market? Build defenses for users' hosted DMSes. One defense for example could map a BYODA behavior and deny queries that are considered anomalies. Another would be to allow DMS access only from BYODA
  5. Well, when installed on-premises it's possible to monitor extra requests or increase of file system size. When it's not on-premises, it's a problem.

Benefits

Getting Practical

Ok, let's get our hands dirty with the proof of concept. It's everything listed in the repository README, but I've cloned it here. These are the steps:

  1. Requirements
    • Ruby 2.3+
    • Docker
    • cURL
  2. Set up MySQL Databases
    • One for BYODA (this application)
    • One for User A
    • One for User B
  3. For each user
    • Create User
    • Let BYODA create tables on the user specified MySQL
    • Create a Task
    • List Tasks

Getting your hands dirty

Initialize all 3 MySQL containers:

# Create MySQL containers
docker run --name mysql-poc -d -e MYSQL_ROOT_HOST=172.17.0.1 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes mysql/mysql-server;
docker run --name mysql-poc2 -d -e MYSQL_ROOT_HOST=172.17.0.1 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes mysql/mysql-server;
docker run --name mysql-poc3 -d -e MYSQL_ROOT_HOST=172.17.0.1 -e MYSQL_ALLOW_EMPTY_PASSWORD=yes mysql/mysql-server;

# Await for them to initialize
sleep 90;

# Create Databases
docker exec -it --user=root mysql-poc mysql -u root -D mysql -e "CREATE DATABASE byoda;";
docker exec -it --user=root mysql-poc2 mysql -u root -D mysql -e "CREATE DATABASE byoda2;";
docker exec -it --user=root mysql-poc3 mysql -u root -D mysql -e "CREATE DATABASE byoda3;";

# Retrieve IP Addresses
export MYSQL_POC_IP=$(docker inspect -f '' mysql-poc);
export MYSQL_POC_IP2=$(docker inspect -f '' mysql-poc2);
export MYSQL_POC_IP3=$(docker inspect -f '' mysql-poc3);

Testing the PoC:

# Solve dependencies for 'byoda.rb'
bundle install

# Start Application
# Requires access to "MYSQL_POC_IP" env variable
ruby byoda.rb &

# Create BYODA "User table" on "mysql-poc" container
# Used to store users' DMS credentials
curl http://localhost:8080/setup --data ""
# {
#   "success": true
# }

# Create "User A"
text=''
text="${text}email=a@a.com"
text="${text}&password=letmein"
text="${text}&dms_adapter=mysql2"
text="${text}&dms_host=$MYSQL_POC_IP2"
text="${text}&dms_username=root"
text="${text}&dms_password="
text="${text}&dms_database=byoda2"
curl http://localhost:8080/users --data $text
# {
#   "success": true
# }

# Setup "User A" DMS
# This operation creates the "tasks" table
curl http://localhost:8080/users/1/setup --data ""
# {
#   "success": true
# }

# Create task for "User A"
curl http://localhost:8080/users/1/tasks --data "task[title]=MyTitle&task[description]=Something"
# {
#   "success": true
# }

# Load tasks for "User A"
curl http://localhost:8080/users/1/tasks
# [
#   {
#     "id": 1,
#     "title": "MyTitle",
#     "description": "Something"
#   }
# ]

# Create "User B"
text=''
text="${text}email=b@b.com"
text="${text}&password=letmein"
text="${text}&dms_adapter=mysql2"
text="${text}&dms_host=$MYSQL_POC_IP3"
text="${text}&dms_username=root"
text="${text}&dms_password="
text="${text}&dms_database=byoda3"
curl http://localhost:8080/users --data $text
# {
#   "success": true
# }

# Setup "User B" DMS
# This operation creates the "tasks" table
curl http://localhost:8080/users/2/setup --data ""
# {
#   "success": true
# }

# Create task for "User B"
curl http://localhost:8080/users/2/tasks --data "task[title]=MyTitle2&task[description]=Something2"
# {
#   "success": true
# }

# Load tasks for "User B"
curl http://localhost:8080/users/2/tasks
# [
#   {
#     "id": 1,
#     "title": "MyTitle2",
#     "description": "Something2"
#   }
# ]

# List all users
curl http://localhost:8080/users
# [
#   {
#     "id": 1,
#     "email": "a@a.com",
#     "password": "letmein",
#     "dms_adapter": "mysql2",
#     "dms_host": "172.17.0.3",
#     "dms_username": "root",
#     "dms_password": "",
#     "dms_database": "byoda2"
#   },
#   {
#     "id": 2,
#     "email": "b@b.com",
#     "password": "letmein",
#     "dms_adapter": "mysql2",
#     "dms_host": "172.17.0.4",
#     "dms_username": "root",
#     "dms_password": "",
#     "dms_database": "byoda3"
#   }
# ]

That's all for today. Thank you.


Share the knowledge :)

Share on Twitter Share on Facebook Share on Google Plus Share on LinkedIn Share on Hacker News