In the Ruby on Rails has_secure_token
gem/feature, it creates a unique token upon record creation and stores it in the database as plain text. If I am using that token to grant users access to an API, is there a security risk in storing that token as plain text in the database?
I was hoping there would be a way to encrypt the token
column when the has_secure_token
method commits the token to the database, similar to how bcrypt
encrypts passwords into a database.
I have tried using gems such as attr_encrypted
to store a hashed value of the token, but it seems to be incompatible with has_secure_token
. Here is how my model is currently set up:
class User < ApplicationRecord
has_secure_token :token
# attr_encrypted :token, key: :encrypt_token, attribute: 'token'
# def encrypt_token
# SecureRandom.random_bytes(32)
# end
end
The commented code is attr_encrypted
code that has proven to be incompatible. If someone knew if there was a way to safely encrypt a column in the database, while also using has_secure_token
, I would greatly appreciate it!
Let me know if any more information is needed or this is confusing. Thanks in advance!
Rails 6 ActiveModel::SecurePassword#has_secure_password accepts an attribute name argument and will use BCrypt to set the value of a corresponding #{attribute}_digest
column. The default attribute name is password
and the model must have an accessor for a #{attribute}_digest
attribute.
A simplified example with both a password and an api_token:
rails generate model User password_digest:string api_token_digest:string
Rails 6
class User < ApplicationRecord
has_secure_password #create a `password` attribute
has_secure_password :api_token, validations: false
before_create do
self.reset_token = SecureRandom.urlsafe_base64
end
end
Prior to Rails 6 you can directly invoke BCrypt to encrypt the token.
require 'bcrypt'
class User < ApplicationRecord
has_secure_password #create a `password` attribute
before_create do
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
self.api_token = SecureRandom.urlsafe_base64
self.api_token_digest = BCrypt::Password.create(api_token, cost: cost)
end
end