I'm using the attr_encrypted gem and I got also devise installed in my environment.
I got a user model this is handled by devise and the database column is:
encrypted_password
Users can save clients and I want to encrypt the clients name and age with the users password.
my client.rb file looks like this:
Here the data gets encrypted successfully.
class Client < ActiveRecord::Base
attr_accessor :name :age
attr_encrypted :name, :age, key: "test1234"
But I'd like to encrypt the data with the Users.password.
Something like so:
class Client < ActiveRecord::Base
attr_accessor :name :age
attr_encrypted :name, :age, key: current_user.encrypted_password
The current_user is the Devise helper method but since this is from a session I can't access it in a model.
Basically I'd like to encrypt all the clients stuff with users password.
But If I do that with the encrypted_password then I already got the password to decrypt the whole field.
I want to provide security to my users and I don't want to know or be able to view their data.
So the only way to do this is by encrypting all the data with the prehashed devise users password?
edit:
The user.encrypted_password is already hashed and whenever I access the db - I can use this to decrypt all the data right?
So I should request the users password -> hash it like devise does - compare it with the users.encrypted_password?
Do I have a logic error somewhere ?
How would you solve this?
attr_encrypted provides a way to specify an instance method to provide the key.
class Client < ActiveRecord::Base
attr_encrypted :name, :age, key: :client_key
def client_key
# just assuming relation between Client and User
self.user.encrypted_password
end
end
Source: https://github.com/attr-encrypted/attr_encrypted#symbols-representing-instance-methods-as-keys
As you using Devise it uses bcrypt algorithm to encrypt your password which is one way encryption
ie this process is not reversible, there's no way to go from the hash back to the password.
so you can use that hash for encrypting the whole data.
But my suggestion would be you use bcrypt algorithm for encrypting your data rather than using user password,reason why i am suggesting bcrypt rather than using your password a hash to encrypt your data
You will have re-encrypt you data each and every time when the user
changes his password If you fail to do so in any occasion you wont
be able to retrive you data back.
The overhead will more ie each time re-encrypting the data on
password change
The encrypted_password will be very tightly coupled with the user
data. I feel that the user data should be independent of password
related to access and there should be a different independent
encrypting for use data which is not related to user login or
password
You can also ref : https://github.com/codahale/bcrypt-ruby
Related
I'm using Rails 4 and Devise with Devise SAML Authenticatable for my Account system.
I've got the SAML working and all, but am trying to work out one thing.
I'd like to change one of the SAML attributes before saving it (since it is formatted incorrectly). Essentially, the Account's SAML request is given a role attribute which is one of the following Group_admin, Group_consumer, Group_supplier. I have a role field in my Account model enumerated as follows:
enum role: [:admin, :consumer, :supplier]
Clearly I can't directly set role because Group_admin != admin (etc.). Is there a way to modify the SAML attribute that is given before Devise saves the field?
I've tried a before_save filter to no avail.
before_save :fix_role!
private
def fix_role!
self.role = self.role.split('_')[1]
end
Does anyone know of a way to do this? I can post any other code if necessary, I'm just not sure what else is needed. Thanks.
I was able to do the following to fix the problem:
attribute-map.yml
"role": "full_role"
account.rb
before_save :set_role!
attr_accessor :full_role
private
def set_role!
self.role = self.full_role.split('_')[1]
end
Essentially, I used an attr_accessor to store the incorrectly formatted role given from the SAML response and a before_save filter to correctly set the "real" role field.
I've a password stored in a database.
The user should be able to set a new one.
I've also used attr_encrypted gem to store it as encrypted text (symmetric because i need it back to connect to other services and not just to login) but this is another story.
When I show the form, the default behavior of ruby helpers is to not send back the password to the browser for obvious security reasons.
Here is the code:
<%= f.input :app_password, :as => :password %>
this behavior might be ok, nevertheless some issues arise from it.
if the user saves the form with null password, the password will be erased. I should test and avoid null-password savings but in some application null password is acceptable and doing so i would prevent this possibility.
it doesn't provide a visual feedback to the user on the fact that he or she has compiled the password field.
it doesn't play very well with validations
What are my options here in order to obtain the most standard possible behavior (that is, no 'change password' checkboxes)?
Please, in forms include password fields this way:
<%= f.password_field :app_password %>
The standard use and set of password is:
Add to your user model a call to has_secure_password
Add field to users for store an encripted password, migration: add_column :users, :auth_token, :string
Before create generate a token:
begin
self.auth_token = SecureRandom.urlsafe_base64
end while User.exists?(auth_token: self.auth_token)
Authenticate your users with #user.authenticate(params[:password])
Update password at another form with 3 fields, old_password, password, and password_confirmation.
I'm using the "has_secure_password" way to store a secure password in the database. When creating an user by the admin (in my app users are created, users can't create an account themselves), in the user model the password_digest is filled by a method to create a random password (see code). When the record is then saved, it is saved secure. So the user method is creating a password_digest say "TY5665^%^", then it is saved in the database say "Y^6&$d%$56GFT". Great!
before_validation :create_random_password, :on => :create
def create_random_password
self.password_digest = SecureRandom.hex(5)
end
But when the new user logs in and changes his password in his profile, the new password gets saved OK, but unsecured! Say the user is changing it to "password1", it also gets saved as "password1" in the database. So why is the secure password working on create, but not on update?
Without seeing your update code, make sure your update is to :password, not :password_digest. The magic behind the creation of a password hash to go into :password_digest only starts with :password.
Surprised that you can save your own password directly onto :password_digest and it would work when authenticating. I'd think it would take the password provided by the user, hash it, and then compare the hash to :password_digest (which couldn't be their password).
This is what I do, which may solve the issue:
before_create :set_temporary_password
def set_temporary_password
self.temporary_password = SecureRandom.hex(5)
end
Send the user self.temporary_password, and when they update it, change temporary_password to nil. Then you can know when a user has a temporary password that requires changing.
I've got a User model that is utilizing mongoid. The model has a password, password_confirmation and encrypted_password field. The password and password_confirmation fields are populated at runtime with the value the user would type on the screen when creating a new user. When I persist, I don't want to persist the unencrypted password values, I only want to persist the value contained in encrypted_password. Is this possible? Is there something I can use to denote certain fields as not being persistable?
Thanks in advance
Chris
Here's a way:
Model only needs the password field and use a before_filter:
def User
before_save :hash_password
attr_accessible :password, :password_confirmation
def hash_password
#todo: improve by adding a salt
self.password = Digest::SHA1.hexdigest(self.password)
end
end
Notes:
Passwords should be stored using a one-way hash, and so passwords should not be 'decryptable'
Use a salt (a random value) and add that to the password before passing it to the hexdigest(). Store the salt in the database as well - say a column called password_salt.
password_confirmation is a virtual attribute and does not need to be defined in the model (rails will manage the details internally)
I'd like to implement a Rails User model that has a DB column called password. I want to make it so that when I call...
user_instance.password = 'cleartext'
the method hashes the cleartext before setting it on the instance like so:
Digest::SHA1.hexdigest(cleartext)
I've tried using a callback, but the problem is that is hashes the pw every time the user is saved, even if the pw isn't updated. So it gets hashed and rehashed over and over.
I tried redefining the password= method...
alias password= old_password=
def password=(cleartext)
old_password=(Digest::SHA1.hexdigest(cleartext))
end
but got an error saying password= does not exist.
FYI, you may want to check out restful_authentication plugin as it will do this for you. Why roll your own?
The way acts_as_authenticated does it:
Database/model has a column called "encrypted_password"
Create a virtual attribute called password
Password isn't populated on a find(..) so if the password is blank, don't encrypt
If the password is nonblank, that means the user entered one, so go ahead and encrypt and stuff in encrypted_password
Code snip (random copy-paste from my user class, so don't blindly paste this in):
require 'digest/sha1'
class User < ActiveRecord::Base
# stuff
# callback
before_save :encrypt_password
attr_accessor :password
# methods
def encrypt_password
return if password.blank?
salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
crypted_password = Digest::SHA1.hexdigest("--#{salt}--#{self.password}--")
end
In response to Ethan's comment, what is a virtual attribute, a virtual attribute is one that is not in the database, like Matt's attr_accessor :password, this way you can accept that input from the user, but no necessarily store it in that form, in this instance we want to be able to accept a clear text password, but we want to store it encrypted. To do this we have a virtual attribute :password, and in the database we store it as encrypted_password.
Well, you could override the setter:
def password=(value)
self[:password] = Digest::SHA1.hexdigest(value)
end
And this would always encrypt the value. You don't need a before_save or an attr_accessor.
Note that it might be a good idea to choose a random n-bit value per user (upon creation of that user) and prepend that to the password when you hash it.
The reason being: if anyone gets a hold of your database, not only can't they immediately see the users' password, they also can't see if two users have identical passwords (important if one of those users grabs your database) and a certain class of hash-cracking attacks (rainbow tables) becomes harder.