thetalabs/laravel-database-encryption

There is no license information available for the latest version (1.0.6) of this package.

A no fuss Laravel model trait to add encrypted model attributes with the option of blind indexes.

1.0.6 2019-09-13 05:20 UTC

This package is auto-updated.

Last update: 2022-08-13 12:45:15 UTC


README

pipeline status coverage report

A no fuss Laravel model trait to add encrypted model attributes with the option of blind indexes.

Model attributes that are marked as encrypted will be encrypted via Laravel's default encryption.

The library also supports blind indexing on encrypted attributes. Blind indexing hashes the value before encryption and stores it in a separate field. This hash can then be queried using whereEncrypted() and orWhereEncrypted()

Lastly, you can disable blind indexes on a per field basis as well as change the hashing algorithm to your liking.

Note - this trait does not create DB columns for you. You must create your own migrations

Features

  • Encrypts - designated attributes are encrypted when set.
  • Decrypts - automatically decrypts attributes on access.
  • Performant - automatically caches decrypted values once accessed to increase performance.
  • Searching (optional) - hashes original input to support exact searching. Supports fuzzy-ish searching. See section below.
  • Customizable - customizable hash algorithm and field naming.
  • Secure - automatically hides hashes to prevent brute forcing and data leaks.

Requirements

This package requires Laravel 5.6. Earlier versions of Laravel have a different model implementation that is not compatible with this package.

Installation

composer require thetalabs/laravel-database-encryption

Usage

Add the trait to your model

You'll also need to configure the columns you want to encrypt.

This example will encrypt the ssn attribute without hashing it in anyway. In this case, it would not be searchable.

use ThetaLabs\DbEncryption\HasEncryptedAttributes;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    use HasEncryptedAttributes;
    
    // Encrypt the following attributes
    protected static $encrypted = [
        'ssn',
    ];
}

Note that if you would like to enable blind indexing for searching, you can do so like this. These indexes are entirely optional and no hashed data will be stored if the option is disabled.

protected static $encrypted = [
    'ssn' => 'bi'
    // or with a custom bi field...
    'ssn' => 'bi:custom_bi_field'
];

By default, blind indexes are hashed with sha256. You can change this per attribute.

protected static $encrypted = [
    'ssn' => 'bi,hash:sha1',
    // or with custom bi field...
    'ssn' => 'bi:custom_bi_field,hash:sha1'
];

See hash_hmac documentation for supported hashing algorithms.

Add Encrypted Database Columns

Create a migration to update the length and types of your columns.

Note: length is depending on the hashing algorithm you decide to use (if any) for blind indexes. By default sha256 has a length of 64.

    // in your migration...
    $table->string('ssn', 9);
    $table->string('ssn_bi', 64);

Searching

A local scope has been added that allows you to query encrypted fields using the blind index columns.

// searches all users where ssn_bi (or custom bi field)
// is 000-00-0000
$user = User::whereEncrypted('ssn', '000-00-0000')
    ->first();
     
// or...

// Search by name or where ssn is 000-00-0000
$users = User::where('name', 'Thomas Krause')
    ->orWhereEncrypted('ssn', '000-00-0000')
    ->get();

Hiding Encrypted Fields

Typically you'll want to limit decrypting values when they aren't needed.

You should use Laravel the built-in $hidden and $visible attributes for this.

class User extends Model
{
    use HasEncryptedAttributes;
    
    // Encrypt the following attributes
    protected static $encrypted = [
        'ssn',
    ];
    
    // Hide the encrypted attributes
    // will prevent them showing in toArray() and JSON
    protected $hidden = [
        'ssn'
    ];
}

Fuzzy-ish Searching

We also support field splitting in models which are stored along with the original value in the blind index. Note that all values are hashed with the configured hash.

This enables you to define how your data should be split and allows searching on the split pieces as well as the original.

Define a function with the following naming convention to enable the feature. You must return an array with each of the values you want to be searchable. The values returned be in plain text we'll handle the rest.

Naming convention for fields is explodeAttributeToSearchables. Where attribute is the field name. Example for field ssn function should be explodeSsnToSearchables.

Similarly and field named buy_me_a_beer would have a explode function named explodeBuyMeABeerToSearchables.

class User extends Model
{
    use HasEncryptedAttributes;
    
    // Encrypt the following attributes
    protected static $encrypted = [
        'ssn' => 'bi,
    ];
    
    // Can be anything you want here
    // name must match convention
    // you must return an array
    protected function explodeSsnToSearchables($ssn)
    {
        return explode('-', $ssn);
    }
}

Gotcha's

For searching, blind indexes are computed at the time the attribute is set. If you want to search on a column after it's encrypted, you'll need to make a migration to re-encrypt all the values to compute the hashes.

// Something like...
$users = User::all();

foreach ($users as $user) {
    // Will force re-encryption and set ssn as dirty.
    // will also update the hash for the field if it's been set
    $user->ssn = $user->ssn;
}