Is it safe to set password field as attr_accessible? - ruby-on-rails

attr_accessible :email, :password, :password_confirmation
If not, can you please give example of method which prevents 'undefined' error when attr_accessible is removed.

It is safe. Attr_accessible is only dangerous for attributes that control your application logic. For example, if you have a flag that says "yes I've checked this user is an admin", and it can be set by the user instead, because it's attr_accessible, then it's a vulnerability.
Since the password is a piece of information that is provided by the user anyway, making it settable by the same user does not change anything.

If you are security paranoid, you could do this is defining the method password:
def password
self.password
end
this way the password can't be set by hand.
But you shouldn’t worry about it because many login gems like devise needs password in attr_accessible.

Related

rails, provide visual feedback on a password, yet keeping it secure

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.

Mass-assignment issue

Please, explain, how can I make records in database with several methods, changing attributes I DON'T want to be attr_accessible.
For example, in User Model:
attr_accessible :email, :password, :password_confirmation, :guest
I don't want 'admin' true or false row be placed here because of security issue
You can assign the attribute manually, for example if your model is named User you can do the following :
user = User.first
user.update_attributes(attributes_hash)
user.admin = true
user.save
attr_accessible is used only for mass assignment via update_attributes for example, but you can always assign a specific property by calling it directly like in my example above.
I think you may be asking how to change the admin attribute in your test or development environment without adding it to attr_accessable.
One way would be the toggle method. For example,
user = User.first
user.admin?
=> false
user.toggle!(:admin)
user.admin?
=> true
A couple of things about toggle to consider. The attribute must be passed as a symbol and all callbacks and validations are skipped. In other words use caution for anything outside of testing and development. So this is how you can mass-assign the admin attribute without adding it to attr_accessable.

How can I use validation rules set in a model in several controllers in ruby on rails?

For the sign-up form for my website I don't require that users confirm their passwords so there isn't a password confirmation field.
However when a user resets their password and they click the link in their email and then taken to a change password page I require they confirm their password and provide a field for them to do this.
My question is how can I add password confirmation and min and max lengths for passwords with out it affecting my sign up form because they use they same model. For my signup form I have min and max length validation rules and want these to apply to the change password form but also include password confirmation.
What is the best and most convenient way to do this in your opinion?
If password confirmation should only be done for users already created, the simplest solution is :
class User < ActiveRecord::Base
validates_confirmation_of :password, :unless => :new_record?
end
So it will only call the validation when setting the value for new users and not for users trying to sign up.
You can create your own methods to validate model data, something like this:
class User < ActiveRecord::Base
validate :user_data_validation
def user_data_validation
errors.add(:password_confirmation, "error in the password confirmation") if
!password.blank? and password != password_confirm
end
end
On the other hand you can use control-level validations, but:
Controller-level validations can be tempting to use, but often become
unwieldy and difficult to test and maintain. Whenever possible, it’s a
good idea to keep your controllers skinny, as it will make your
application a pleasure to work with in the long run. (c)

In Rails 3, how can I skip validation of the password field when I'm not attempting to update the password?

My User model contains :name, :email, and :password fields. All 3 have validations for length. An "update account" web page allows the user to update his name and email address, but not password. When submitted, params[:user] is
{"name"=>"Joe User", "email"=>"user#example.com"}
Note there is no "password" key because the form doesn't contain such an input field.
When I call
#user.update_attributes(params[:user])
the password validation fails. However, since I'm not attempting to update the password, I don't want the password validation to run on this update. I'm confused why the password validation is running when params[:user] doesn't contain a "password" key.
Note that I want to have a separate web page elsewhere that allows the user to update his password. And for that submission, the password validation should run.
Thank you.
My application does something like this
attr_accessor :updating_password
validates_confirmation_of :password, :if => should_validate_password?
def should_validate_password?
updating_password || new_record?
end
so you have to model.updating_password = true for the verification to take place, and you don't have to do this on creation.
Which I found at a good railscast at http://railscasts.com/episodes/41-conditional-validations
In your user model, you could just ignore the password validation if it's not set.
validates_length_of :password, :minimum => N, :unless => lambda {|u| u.password.nil? }
Using update_attributes will not change the value of the password if there is no key for it in the params hash.
Validation doesn't run against the changed fields only. It validates existing values too.
Your validation must be failing because the password field contains some invalid content that's already saved in the database. I'm guessing it's probably because you're hashing it after validation and you're trying to validate the hashed string.
You can use a virtual attribute (an instance variable or method) that you validate with a custom method, and then assign the hash to the stored password field. Have a look at this technique for ideas.
An app that I am working on uses the following:
validates_confirmation_of :password,
:if => Proc.new { |account|
!account.password.blank?
|| !account.password_confirmation.blank?
|| account.new_record? }
Depending on your requirements, you might want to remove the new_record? check
When password is added then only confirmation will be called and presence will call on create action only
**validates_presence_of :password, :on =>:create**
**validates_confirmation_of :password**

How can I cause a setter method to encrypt data before setting it in ActiveRecord?

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.

Resources