Using Acra to Protect Your Rails App

Note: This tutorial is an extensive step-by-step guide for those who have never used Themis and Acra before. There is also a much shorter version for the experienced Acra users. If it’s your very first encounter with Acra, keep reading.

Intro

Acra is a database security suite, which protects you against data leaks and many typical application threats through strong selective encryption and intrusion detection capabilities.

It is based on our Themis cryptographic library with extreme security and usability for developers in mind (you can read more about Themis' cryptography in Acra here.

Acra is most useful for:

Selective protection of sensitive data,

Autosharded databases,

Microservices,

Situations with severe time constraints (aka "pressing deadlines" ;).

Acra is developer-oriented, with convenient infrastructure, and easily provides strong security and full granular control over your data.

The main basic components of Acra are:

AcraServer - a separate daemon that runs in an isolated environment (separate virtual machine or physical server), which is responsible for holding all the secrets required to decrypt the data and for actually decrypting this data.

AcraWriter - a client-side library, which integrates into the app flow either through ORM or directly, and provides the means to encrypt the sensitive data via generating AcraStructs.

AcraProxy - a client-side daemon that runs under a separate user / in a separate container, and which acts as a database listener that redirects all the queries to AcraServer and feeds the results back to the app.

This tutorial guides you through a typical process of integrating Acra into an app running on Ruby on Rails framework. It is based on the popular example many Ruby users start their development learning with – rubygems.org repository.
RubyGems is a package manager for Ruby that provides a standard format for distributing Ruby programs and libraries in a self-contained format called a "gem".
We’ll integrate Acra into it to provide cryptographic protection of the gem descriptions – i.e. author name, email, app description, etc.

Security model

Acra provides selective encryption and only protects the records you want to protect.

With AcraWriter, the records to be encrypted are wrapped in a function that outputs an AcraStruct (cryptographic container decryptable by AcraServer). AcraStruct is then stored in a database.

In Acra’s threat model, we assume that anything but AcraServer can be compromised and that any piece of data can leak outside. For Acra to stay secure, only AcraServer must stay secure. However, if AcraServer is compromised, the whole implementation of Acra will make no sense.

With Acra we strive to provide 2 main programmatic security guarantees:

G1: Even if all the other parts of the system are compromised, as long as AcraServer is secure, the attacker won’t collect enough data for decryption of the protected materials (database entries in our case).

G2: If the attacker alters the app’s behaviour in such a way that makes it request all the protected (encrypted) data from the database, AcraServer detects it and reacts accordingly by triggering pre-set alarms and panic blocks. This is currently carried out with the help of poison records in the database, which would never have been called up - except for an event of a hack/breach. In the future, more intrusion detection features besides poison records are planned.

If it is explicitly stated that the output for Zone ID must precede the AcraStruct, Acra Server will search for certain strings called Zone IDs (“Zones”) when analysing the database output stream.
Zones let Acra know that within this record a private key corresponding to the Zone ID should be used for the actual decryption of AcraStructs.

Zones are the way to cryptographically compartmentalise records in an already-encrypted environment. They rely on different private keys on the server side. When using Zone keys, you get an additional 3rd guarantee:

G3: If the attacker manages to compromise the system and modify the app to extract all the records protected by Zone keys, he/she will have to reverse-engineer both the storage model and the Zone identification to be able to request all of them correctly.

Deploying infrastructure

In a perfect world, you’d be running different elements of Acra, as well as your database, on 3 separate machines - one for PostgreSQL, one for AcraServer, and one for AcraConnector + AcraWriter + your Rails app.
This implies an immediate introduction of Acra into a real production setting, which we realise is far from a realistic course of events (at least on your very first try of Acra). So this tutorial is more focused on the things you can hands-on try out and tinker with on just 2 or even 1 machine through the creation of different users and containers (i.e. Docker, a test-only implementation of which for Acra is described at the end of this tutorial ("If you want to try Acra in containers)).

In this detailed architectural scheme we see how components of Acra, application, and database relate and interact with each other:

Put simply, the application talks to AcraConnector. AcraConnector pretends to be a database listener that uses standard PostgreSQL protocol and send the request to AcraServer using Secure Session (socket protection protocol). AcraServer sends a request to the database using the regular PostgreSQL protocol and receives an answer.
If AcraServer detects the presence of AcraStruct while parsing the answer, it attempts to decrypt it and replace AcraStruct with a plaintext result in the answer (if decryption is unsuccessful, AcraServer will forward the answer as is). AcraServer returns the data to AcraConnector which returns it to the application.
If Zones are used, AcraServer will use a corresponding private key to decrypt the next detected AcraStruct upon detecting ZoneId. AcraServer will ignore the AcraStruct if no Zone ID is detected before the AcraStruct.

All the dependencies mentioned in this tutorial need to be installed on all the machines/containers running Acra (or for both separate users of AcraServer and AcraConnector), unless it’s explicitly specified that some Acra components should only be installed for a separate user/machine running AcraServer or AcraConnector. All the commands starting with 'go' are meant to be executed from 'acra' folder (the folder with the repository code) on any machine.

In this tutorial, we assume that you have a fully operational PostgreSQL (as Acra transfers and receives data “as is” to and from PostgreSQL database and then processes it - parses the messages - according to the PostgreSQL protocol) and operate on Linux machines (with with Ruby environment and Rails framework installed). It’s also crucial that you have libssl-dev installed with libcrypto.so in $PATH before proceeding.

Note that before PostgreSQL 9, only escape_bytea mode was present. Now the hex mode is used by default. If your PostgreSQL uses escape_bytea, set this parameter during the installation process using the following command:
$GOPATH/bin/acra-server --db_host=127.0.0.1 --escape_bytea

This might appear complicated, but in reality, Acra is easy to install. Let’s go!

Step 1. Install GoLang dependencies:

sudo apt-get install git golang libssl-dev make build-essential

and set your $GOPATH to the place where you will store the code.

Step 2. Install Themis

Step 3. Build keymaker:

go get github.com/cossacklabs/acra/cmd/acra-keymaker

It’s preferable to build the key maker and generate the keys on the machine that’s going to host AcraServer. This is the secure way to deal with the keys, as according to Acra’s threat model, we expect that any data stored outside of AcraServer can be compromised. Which is why we strongly advise against using all the components of Acra on a single machine (i.e. with the help of Docker containers) for anything more serious than testing and tinkering.

Here yourID is a placeholder for the ID name of your choice. You’re allowed to use 5-256 symbols (inclusively) that include Latin symbols, numbers, “-” (minus symbol), “_” (underscore), and “ ” (space).

The generator will generate and place the keys into the .acrakeys directory (you can change this with --output argument).

For a few minutes, let the keys rest where they are - they will be necessary after you have installed AcraServer, AcraProxy, and AcraWriter (if you’d like to read more about the keys, please see Key Management).

AcraServer

Yet another reminder that AcraServer needs to be installed on a separate computer/virtual machine/container and with SSL turned off in PostgreSQL.

AcraServer should have AcraConnector's public transport key, and Acra Server's public transport key must be given to AcraConnector.This is necessary for accepting connections via Secure Session from clients.

The command above can be complemented with --db_port=5432 -v to adjust the listener port and to add logs quickly. There are more parameters available, and you can find them in the documentation page for Acra, but for the present goal - namely, for an easy integration of Acra into a Ruby app, the default parameters will do.

You can also run with the options from config. Copy the example config:

$REPO_DIR/configs/acra-server.yaml

or from

$GOPATH/src/github.com/cossacklabs/acra/configs/acra-server.yaml

Or generate the config yourself:

$GOPATH/bin/acra-server --dump_config > acra-server.yaml

and run:

$GOPATH/bin/acra-server --config_file=acra-server.yaml

Proper logging is set with:

$GOPATH/bin/acra-server --db_host=127.0.0.1 -v

You’ll also need an explicit server_id that is used as an identifier for Secure Session between AcraConnector and AcraServer:

By default, AcraServer listens on port 9393, but you can set a custom port (just don’t forget to follow the instructions on specifying a custom port for AcraConnector further in this tutorial) if there is a need:

Step 6. Build AcraConnector

AcraConnector should run under a separate user on the same machine as your Rails app (or in a separate container for testing purposes). All the database requests need to be directed to AcraConnector, which then pretends to be a database listener (a local proxy) and relays all the requests to AcraServer (on a separate machine/in a separate container), listens back and returns the responses to the app.

Install AcraConnector:

go get github.com/cossacklabs/acra/cmd/acra-connector

Put .acrakeys/yourID + .acrakeys/yourID_server.pub to AcraConnector’s key folder (.acrakeys or anything you chose in --keys_output_dir). AcraConnector’s own public key should already have been given to AcraServer to establish Secure Session connection. Pre-shared public keys enforce maximum secrecy and easy-to-manage authentication, and - as you can see - require minimal intervention on your side or into your code for successful implementation.

Upon launch with the default settings, AcraConnector will start listening on port 9494 and will attempt to connect to AcraServer on port 9393. To initialise, use:

Now AcraConnector will be listening on port 5432 (your custom port).
This is important if you want the launch of AcraConnector to be transparent for the app (if your app connected to PostgreSQL on 5432).

You can also launch AcraConnector with the options from example config - just copy it from:

$REPO_DIR/configs/acra-connector.yaml

Or from:

$GOPATH/src/github.com/cossacklabs/acra/configs/acra-connector.yaml

If you want, you can generate an example config yourself. Use:

$GOPATH/bin/acra-connector --dump_config > acra-connector.yaml

And run:

$GOPATH/bin/acra-connector --config_file=acra-connector.yaml

These instructions should be enough to get you up and running with AcraConnector, and you can proceed to the actual integration of Acra into a Rails app. For more advanced ways of setting up and launching AcraConnector, please see the corresponding fragment of Acra documentation.

Integration

Step 7. Integrating Acra with Rails

Since in our example we’ll be integrating Acra into a Ruby web app - namely RubyGems app running on Ruby on Rails framework - to protect the gem descriptions, i.e. name of the app author, email, summary, license, app description, etc.. Here, for example, is what our own rubygem specification for Acra looks like:

To protect similar fields in your own gems, you need to start with cloning the rubygems.org repository (to the same machine/container that is running AcraConnector) with:

git clone https://github.com/rubygems/rubygems.org.git

Visit your local RubyGems sign up page and register with your email address, username, and a password. After the registration, verify your email using the following command: psql -h127.0.0.1 --dbname=acra -Utest -c 'update users set email_confirmed=TRUE where id = (select max(id) from users);'

AcraWriter comes into play next. It is basically Themis that is generating AcraStructs with the keys you've made available to AcraWriter. You can encrypt your sensitive data by generating AcraStructs with AcraWriter anywhere in your app. AcraWriter can be used whenever you need to encrypt sensitive records (in our case - gem descriptions).

Install AcraWriter:

gem install acrawriter

Step 7.1 Install activerecord_acrawriter

Note: This only works with Ruby > 2.2 because that is a requirement of Active Record

This completes the process of integrating a fully operational Acra into your Ruby on Rails app in a real-life setup that uses different machines/users.

Result and testing

To test your Acra setup:

Connect AcraConnector to AcraServer, send a regular request to your database through AcraConnector. If you see the answer, AcraConnector and AcraWriter are able to connect and forward signals back and forth. It means that the network and the keys are fine.

Upon integrating AcraWriter into your code, try generating an AcraStruct from some payload. If you succeed in running AcraWriter code, Themis library is installed properly and the keys are located in the expected places.

Write a row with AcraStruct into the database, either directly or through AcraConnector. Request this row through AcraConnector. If you see the decrypted payload in the response, the scheme works properly.

And now, let’s test the whole setup through pushing a gem with its info protected by Acra. Use the username, email, and password you entered during the registration.

Note: RubyGems saves the credentials in ~/.gem/credentials so you only need to log in once.

It should return you the plaintext data that you’ve entered (author, email, etc). To see that everything is, in fact, encrypted and Acra works properly, send these 2 queries directly to the database, they should return an “incomprehensible” mishmash of symbols in which the data turns after the encryption:

If the database returns all the data it contains in encrypted form, everything is working properly and you’ve successfully integrated Acra into your Ruby app!

Note: A gem can have dependencies and if those are missing from rubygems.org or locally, the names of the unresolved dependencies go to the “unresolved_name”. So the latter example will only return the encrypted data for “unresolved_name” field if there are some names of dependencies added to "unresolved_name". Otherwise, it will return nothing.

If You Want to Try Acra in Containers

We’ve made some special effort to make Acra work with Docker. However, please remember that using Acra with containers is violating its basic security guarantee. Docker is immutable, and zones/keys are not. This means you might want to attach some storage and end up leaving keys accessible to attackers. So use Acra with Docker for testing purposes only.

To simply test the waters of using Acra, you can use pre-made config files and examples below - they can also serve as a reference for integrating Acra with a Ruby app.

You’ll need to download Acra, just like it was described above:

git clone https://github.com/cossacklabs/acra
cd acra

And start AcraServer, AcraConnector, and PostgreSQL in separate Docker containers:

After executing this command, you will have a running PostgreSQL with test:testuser:password with forwarded 5432 port, AcraServer with keys that you generated above, and AcraConnector that forwards port 9494.

Don’t forget to stop your local PostgreSQL if you run it before launching the Docker with PostgreSQL in a container or you’ll get an error from 2 instances of an application trying to listen on the same port.

By default, Docker will create 3 containers with the following names: docker_acra-server_1, docker_acra-connector_1, and docker_pg_1.

Note: Either name your database gemcutter_development (to follow the default rubygems practice or come up with a custom name - in this case the custom name is acra. Don’t forget to change the database name in the
config/database.yml file afterwards. Use this database name in all the following commands below.

You’ll be asked to input the password for PostgreSQL (test) and you’re ready to proceed. Copy the keys that we generated previously:

You see the data is in encrypted form. Congratulations! Acra is now working with Zones!

Conclusion

As you can see, establishing cryptographic protection for the data in your web app with Acra is a very straightforward and simple process. We hope that this tutorial was fun and informative and that you will be using Acra in the future. If you only tried the Docker-based examples, try running Acra in a real world setup - it’s just as convenient.