Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/enquo/active_enquo

ActiveRecord extension for encrypted query operations
https://github.com/enquo/active_enquo

activerecord cryptography encryption enquo hacktoberfest rails ruby security

Last synced: about 2 months ago
JSON representation

ActiveRecord extension for encrypted query operations

Awesome Lists containing this project

README

        

[ActiveEnquo](https://enquo.org/active_enquo) is a Ruby on Rails ActiveRecord extension that works with the [pg_enquo Postgres extension](https://github.com/enquo/pg_enquo) to allow you to query encrypted data.
This allows you to keep the data you store safe, by encrypting it, without compromising on your application's ability to search for that data and work with it.

Sounds like magic?
Well, maybe a little bit.
Read our [how it works](https://enquo.org/how-it-works) if you're interested in the gory cryptographic details, or read on for how to use it.

# Pre-requisites

In order to make use of ActiveRecord extension, you must be running Postgres 11 or higher, with the [`pg_enquo`](https://github.com/enquo/pg_enquo) extension enabled in the database you're working in.
See [the `pg_enquo` installation guide](https://github.com/enquo/pg_enquo/tree/main/doc/installation.md) for instructions on how to install `pg_enquo`.

Also, if you're installing this gem from source, you'll need a reasonably recent [Rust](https://rust-lang.org) toolchain installed.

# Installation

It's a gem, so the usual methods should work Just Fine:

```sh
gem install active_enquo
# OR
echo "gem 'active_enquo'" >> Gemfile
```

On macOS, and Linux `x86-64`/`aarch64`, you'll get a pre-built binary gem that contains everything you need.
For other platforms, you'll need to have Rust 1.59.0 or later installed in order to build the native code portion of the gem.

# Configuration

The only setting that ActiveEnquo needs is to be given a "root" key, which is used to derive the keys which are used to actually encrypt data.

## Step 1: Generate a Root Key

The ActiveEnquo root key ***MUST*** be generated by a cryptographically-secure random number generator, and must also be 64 hex digits in length.
A good way to generate this key is with the `SecureRandom` module:

```sh
ruby -r securerandom -e 'puts SecureRandom.hex(32)'
```

## Step 2: Configure Your Application

With this key in hand, you need to store it somewhere.

### Using Rails Credential Store (Recommended)

The recommended way to store your root key, at present, is in the [Rails credentials store](https://guides.rubyonrails.org/security.html#custom-credentials).

1. Open up the Rails credentials editor:

```ruby
rails credentials:edit
```

2. Add a section to that file that looks like this:

```yaml
active_enquo:
root_key: "0000000000000000000000000000000000000000000000000000000000000000"
```

3. Save and exit the editor. Commit the changes to your revision control system.

### Direct Assignment (Only If You Must)

Using the Rails credential store only works if you are using Rails, of course.
If you're using ActiveRecord by itself, you must set the root key yourself during application initialization.
You do this by assigning a `RootKey` to `ActiveEnquo.root_key`, like this:

```ruby
# DO NOT ACTUALLY PUT YOUR KEY DIRECTLY IN YOUR CODE!!!
ActiveEnquo.root_key = ActiveEnquo::RootKey::Static.new("0000000000000000000000000000000000000000000000000000000000000000")
```

Preferably, you would pass the key into your application via, say, an environment variable, and then immediately clear the environment variable:

```ruby
ActiveEnquo.root_key = ActiveEnquo::RootKey::Static.new(ENV.fetch("ENQUO_ROOT_KEY"))
ENV.delete("ENQUO_ROOT_KEY")
```

Support for cloud keystores, such as AWS KMS, GCP KMS, Azure KeyVault, and HashiCorp Vault, will be implemented sooner or later.
If you have a burning desire to see that more on the "sooner" end than "later", PRs are welcome.

# Usage

We try to make using ActiveEnquo as simple as possible.

## Create Your Encrypted Column

Start by creating a column in your database that uses one of the [available `enquo_*` types](https://github.com/enquo/pg_enquo/tree/main/doc/data_types), with a Rails migration:

```ruby
class AddEncryptedBigintColumn < ActiveRecord::Migration[6.0]
def change
add_column :users, :date_of_birth, :enquo_date
end
end
```

Apply this migration in the usual fashion (`rails db:migrate`).

## Reading and Writing

You can now, without any further ado, use that attribute in your models as you would normally.
For example, you can insert a new record:

```ruby
User.create!([{name: "Clara Bloggs", username: "cbloggs", date_of_birth: Date.new(1970, 1, 1)}])
```

When you retrieve a record, the value is there for you to read:

```ruby
User.where(username: "cbloggs").first.date_of_birth.to_s # => "1970-01-01"
```

## Querying

This is where things get *neat*.

Performing a query on Enquo-encrypted data is done the same way as on unencrypted data.

You can query for records that have the exact value you're looking for:

```ruby
User.where(date_of_birth: Date(1970, 1, 1))
```

Or you can query for users born less than 50 years ago:

```ruby
User.where(date_of_birth: (Date.today - 50.years))..)
```

This doesn't seem so magical, until you take a peek in the database, and realise that *all the data is still encrypted*:

```sh
psql> SELECT date_of_birth FROM users WHERE username='cbloggs';
age
-------
{"v1":{"a":[],"y":[],}}
```

## Migrating Existing Data to Encrypted Form

This is a topic on which a lot of words can be written.
For the sake of tidiness, these words are in [a guide of their own](docs/MIGRATION.md).

## Indexing and Ordering

To maintain [security by default](https://enquo.org/about/threat-models#snapshot-security), ActiveEnquo doesn't provide the ability to `ORDER BY` or index columns by default.
This is fine for many situations -- many columns don't need indexes or to be ordered in a query.

For those columns that *do* need indexes or `ORDER BY` support, you can enable support for them by setting the `enable_reduced_security_operations` flag on the attribute, like this:

```ruby
class User < ApplicationRecord
# Enables indexing and ORDER BY for this column, at the cost of reduced security
enquo_attr :age, enable_reduced_security_operations: true
end
```

### Security Considerations

As the name implies, "reduced security operations" require that the security of the data in the column be lower than [Enquo's default security properties](https://enquo.org/about/threat-models#snapshot-security).
Specifically, extra data needs to be stored in the value to enable indexing and ordering.
This extra data can be used by an attacker to:

* Identify all rows which have the same value for the column (although not what that value actually *is*); and

* Perform inference attacks to try and determine the approximate or exact value for the column of some or all of the rows.

The practical implications of these attack vectors varies wildly between different types of data, which makes it harder to decide if it's reasonable to allow reduced security operations.
Our recommended rule-of-thumb is that if the features you need can *only* be implemented if you either enable reduced security operations, or leave the data unencrypted, then enable them.
Otherwise, leave the default as-is.

## Saving Disk Space

While the power of `ActiveEnquo` is based around being able to *query* encrypted data, not all columns necessarily need to be queried.
If so, you can reduce the disk space requirements for those columns by setting `no_query: true` for those columns:

```ruby
class User < ApplicationRecord
# Disables querying for this column, and saves a heap of bytes on your disk space
enquo_attr :age, no_query: true
end
```

More accurate indications of the disk space requirements for the supported data types can be found in [the description of each data type](https://github.com/enquo/pg_enquo/tree/main/doc/data_types).

# Future Developments

ActiveEnquo is far from finished.
Many more features are coming in the future.
See [the Enquo project roadmap](https://enquo.org/roadmap) for details of what we're still intending to implement.

# Contributing

For general guidelines for contributions, see [CONTRIBUTING.md](CONTRIBUTING.md).
Detailed information on developing `ActiveEnquo`, including how to run the test suite, can be found in [docs/DEVELOPMENT.md](DEVELOPMENT.md).

# Licence

Unless otherwise stated, everything in this repo is covered by the following
licence statement:

Copyright (C) 2022 Matt Palmer

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.