ankane / blind_index

Securely search encrypted database fields
MIT License
634 stars 27 forks source link

Attributes returning nil after implementing blind index and as_json not working as expected #53

Closed pramodshinde closed 3 years ago

pramodshinde commented 3 years ago

I have implemented bling index in combination with Lockbox like following

Added index and column like in migration

    add_column :users, :first_name_ciphertext, :text
    add_column :users,  :first_name_bidx, :string
    add_index  :users,  :first_name_bidx

User Model

 class User < ApplicationRecord
   encrypts :first_name, type: :string, key: "xxxxxxxxxxxxx8748098ed9b8dxxxxxx"
   blind_index :first_name, key: "xxxxxxxxxxxxx8748098ed9b8dxxxxxx"
 end

I backfilled data and following is working fine before dropping origin column :first_name

u = User.where(first_name: 'pramod').first
=> SELECT  `users`.* FROM `users` WHERE `users`.`first_name_bidx` = 'dsadsadsadas/l2SV/hIegoKjnk5QiqgEY2rVYR0jr0='ORDER BY `users`.`id` ASC LIMIT 1
# Getting result as expected and 
u.first_name
=> "pramod"

Though Not returning first_name in JSON

u.as_json
=> {id: 1} # **Not returning first_name in JSON**

After dropping origin column :first_name, u.first_name is nil

u = User.where(first_name: 'pramod').first
u.first_name
=> nil
u.as_json
=> {id: 1} # **Not returning first_name in JSON**

Ruby 2.6.6 rails', '~> 5.2.4' lockbox (0.6.5) blind_index (2.2.0)

Let me know if I am missing something here

ankane commented 3 years ago

Hey @pramodshinde, Lockbox attributes are excluded from as_json by default. You can use:

u.as_json(methods: [:first_name])

For u.first_name returning nil, make sure you've followed the Lockbox backfill instructions.

pramodshinde commented 3 years ago

Hey @ankane thanks for response,

as_json is working when it is given in methods list

u.as_json(methods: [:first_name])

I am not using Lockbox backfill i.e. Lockbox.migrate(User) explicitly but something similar, like

updated_records = []
User.find_each do |recond|
  updated_records << record.send("first_name=", record.send("first_name"))
end

then with active record import

User.import(updated_records,
  validate: false,
  timestamps: false
)

This is because we need to skip all callbacks and to achieve bit faster backfill. Above is successfully migrating/backfilling data and adding ciphertext columns.

Not sure why u.first_name in returning nil as we are not doing anything special.

ankane commented 3 years ago

I'm guessing it's something with the custom backfill code. If u.first_name_ciphertext is nil, u.first_name will return nil.

pramodshinde commented 3 years ago

thanks found issue, In our code base first_name method was overriden , which was causing u.first_name to return nil

Is there any way where I can override encrypted field like

def first_name
  super
  self.first_name.strip_special_characters
end

Inside above method I am getting first_name as nil

P.S. Closing this for now if you have any advice on this that would be great help.

ankane commented 3 years ago

You can use Module#prepend in Ruby to override just about any method, but it's not officially supported by the library. See this comment for code.