Ruby BCrypt hash comparison not working - ruby

I am a new comer to Ruby, so apologies if this question has already been answered. I have read the other questions and still cannot figure out what I am doing wrong.
I am creating hashed passwords for storing in a db like this:
new_user.password = BCrypt::Password.create(unhashed_password)
# Write the user to database
new_user.store_user
I then retrieve the user from the db by checking against the inputed user name, and then check the password like this:
# Get user from the database
def self.get_user(check_user_name)
db = User.open_db
user = User.new
user_arr = db.execute("SELECT * FROM user_data WHERE user_name = ?", check_user_name).first
db.close
# if the user exists check the password
if user_arr.size != 0
print "Enter your password : "
# Get password from user
user_input_password_attempt = gets.chomp
end
# Parse the db user into a user class if password guess is correct
stored_password = BCrypt::Password.new(user_arr[2])
if user_input_password_attempt == stored_password
##users_logged_in += 1
user.user_id = user_arr[0]
user.user_name = user_arr[1]
user.password = user_arr[2]
return user
end
:no_user
end
My problem is that the var stored_password is returning a hash and != user_input_password_attempt
I have read the Ruby-Doc and googled this extensively

When you use == you are actually calling the == method defined on the object on the left hand side, passing the right hand side as argument:
a == b
is equivalent to
a.==(b)
Depending on the object you call the == method you might receive a different result. In other words:
a == b
might or might not return a different result than
b == a
While personally I think this is nonsense and equality operators should be transitive, symetric and reflexive the BCrypt people have decided to implement it in another way:
def ==(secret)
super(BCrypt::Engine.hash_secret(secret, #salt))
end
(taken from http://bcrypt-ruby.rubyforge.org/classes/BCrypt/Password.html#M000009)
This means that you have to write:
stored_password = BCrypt::Password.new(user_arr[2])
if stored_password == user_input_password_attempt
...
end
in order to call the == method on the Password instance.

Related

Ruby update all same object data in hash array for JSON.parse

So a = first is
=> <Ng::EntityConfiguration id: 15903, entity_id: 1, entity_type: "Ng::Company", key: "wpa2.psk", value: "[{"ssid":"Main-Hall-Staff","password":"abc123","dhcp":"Enabled"},{"ssid":"Main-Hall-Guest","password":"def456","dhcp":"Disabled"}]", created_at: "2016-11-08 11:03:51", updated_at: "2016-11-08 11:03:51", name: "WIFI/Main Hall">
I have a.value which is will return:
"[
{\"ssid\":\"Main-Hall-Staff\",\"password\":\"abc123\"},
{\"ssid\":\"Main-Hall-Guest\",\"password\":\"def456\"}
]"
My question is, how to update both password value and save it?
new_pass1 = 'xyz123'
new_pass2 = 'xyz321'
I have tried code (below) but this will only update first password if i only have one hash_array.
Here is my full code
def encrypt_pass
# get the actual password
parse = JSON.parse(self.value)
get_pass = parse.last['password']
# encrypt the password
crypt = ActiveSupport::MessageEncryptor.new(ENV["SECRET_KEY_BASE"])
encrypted = crypt.encrypt_and_sign(get_pass)
# save the new encrypted password
parse.first['password'] = encrypted
encrypt_pass = parse.to_json
self.value = encrypt_pass
end
Just to be clear, you're trying to update both the Main-Hall-Staff password and the Main-Hall-Guest password (all passwords) from your record to be the encrypted version of themselves? I'm assuming this method is called in a before_save callback of some sort? If you show more code related to the model I can give you more details.
def encrypt_pass
# Changed the name to devises, always use easy to understand naming
# Also rescuing from a JSON parse error, this isnt always recommended
# as it hides other errors that might be unrelated to parsing
devices = JSON.parse(self.value) rescue []
crypt = ActiveSupport::MessageEncryptor.new(ENV["SECRET_KEY_BASE"])
devices.each do |device|
# get the actual password
password = device['password']
# encrypt the password
encrypted_pass = crypt.encrypt_and_sign(password)
# Save the encrypted password
device['password'] = encrypted_pass
end
self.value = devices.to_json
end
Hopefully you have some logic surrounding when this method is called as you dont want to encrypt an already encrypted password.

How should I check passwords in Ruby?

I am using Ruby to manage users in a database.
I am using pass = Digest::MD5.hexdigest() to encrypt passwords before they are being added to the database.
I need to create a function to check that a given password matches that stored in the database but I'm not sure how I should do it.
Do I use pass = Digest::MD5.hexdigest() on the user provided password, and then check that against what is returned from the database?
This is correctpassword?:
def correctpassword?(nick, pass)
user = nick
pass = Digest::MD5.hexdigest(pass)
db = SQLite3::Database.new "database.db"
db.execute("SELECT * FROM t1 WHERE user = ? and pass = ?", user, pass)
!results.empty?
end
This is attempting to use correctpassword?:
if clt.registered?(#nick)
if clt.correctpassword?(#nick, #pass)
sv_send 'NOTICE', #nick, ":Correct password."
else
sv_send 'NOTICE', #nick, ":Incorrect password."
end
end
I don't see either notices. Using correctpassword? seems to break things.
This works though:
if clt.registered?(#nick)
sv_send 'NOTICE', #nick, ":This account is registered."
end
There is no assignment for results var. Do results = db.execute("SELECT * FROM t1 WHERE user = ? and pass = ?", user, pass)
MD5 is a one-way hash encryption algorithm. There is no way to directly decrypt a MD5 hash. The algorithm itself uses modular arithmetic to package the serialized string and there is no way to go backwards from that.
So I think you should convert the password to MD5 which user try to enter and compare that hash with stored encrypted password in db.
Eg:
> stored_password_in_db = Digest::MD5.hexdigest('gagan')
#=> "cc18a19beff0bdf874861a4dae6124b6"
> user_enter_password_for_login = Digest::MD5.hexdigest('gagan')
#=> "cc18a19beff0bdf874861a4dae6124b6"
> stored_password_in_db == user_enter_password_for_login
#=> true
> user_enter_password_for_login = Digest::MD5.hexdigest('Gagan')
#=> "f52bb23033354697e8f55abdaed9d94f"
> stored_password_in_db == user_enter_password_for_login
#=> false

Detect duplicate records using Sequel and PostgreSQL

I want to check to see if the email address that a new person uses when registering is a duplicate of an existing record.
Here is the code:
post '/signup' do
email_address = params['email_address']
username = params['username']
password = params['password']
#duplicate = DB[:users].select(:email_address).where('email_address = ?', email_address)
if email_address = #duplicate
redirect "/?msg=Email address already in use. Try again"
return
end
end
This blocks all attempts even when the email address is not a duplicate and redirects with the error message.
If I substitute #duplicate with duplicate (without the # mark) then same result, all attempts blocked. If I use == instead of = then duplicate email addresses are ignored and no attempts are blocked.
I know the query:
DB[:users].select(:email_address).where('email_address = ?', email_address)
is correct because I have tested it. So I assume the problem lies with the construction of the if clause.
You need add .first at the end of you query otherwise you will get an array.
After that you can test if #duplicate != nil
If you write email_address = #duplicate the code inside the if will be always excuted since the condition for the if expression is always true. Anything in Ruby except nil and false evaluates as true in boolean context.
If you write email_address == #duplicate the code inside the if will be never executed since you are trying to compare
a string (email_address) with an object of class Sequel::Postgres::Dataset (#duplicate).
A better approach could be
post '/signup' do
# same code as yours...
#duplicate = DB[:users].where(email_address: email_address).count
unless #duplicate.zero?
# redirect and return
end
end

Why won't Sequel write my database table?

controller/makenew.rb
class MakeController < Controller
map '/makenew'
#require 'model/debate'
def debate
if request.post? #this line is potentially dangerous!
#---> 1/3 fetch postdata
data = request.subset(:question, :type, :category, :assertion)
data['user_id'] = user.id #id = request.params['id']
#---> 2/3 check permissions
if user.points < 40
flash[:error] = 'You don\'t have enough points to make a debate.'
redirect_referrer
else
debate = Debate.new
end
#---> 3/3 modify database
begin
debate.save(data)
flash[:success] = success
flash[:form_data] = debate
redirect 'debates'
rescue => e
Ramaze::Log.error(e)
#flash[:form_errors] = debate.errors
#flash[:error] = data
flash[:error] = e
#flash[:error] = 'Failure whilst saving. Contact technical support!'
redirect 'debates' #redirect_referrer
end
#|
end #closes posting conditional
end #closes makesave
end
The error I get is.
SQLite3::ConstraintException: debates.question may not be NULL
I have checked the postdata for data.question and it is not null.
What is going on?
You need to pass 'data' to #update. Thus:
debate.save(data)
is wrong, you have to do:
debate.update(data)
debate.save
If you don't do this, your debate object has no member assigned and thus its question member is nil, violating your DB constraints.
See the differences between #save and #update here:
Update : http://sequel.rubyforge.org/rdoc/classes/Sequel/Model/InstanceMethods.html#method-i-update
Save : http://sequel.rubyforge.org/rdoc/classes/Sequel/Model/InstanceMethods.html#method-i-save
In a nutshell: #save will save the current model instance to the database, while #update will change a bunch of instance attributes in one operation.
But you have to remember that changing a model instance's attributes DOES NOT write them to the database. You always have to call #save explicitly.
Are you sure that your model accepts mass assignment of primary keys?
Try calling Debate.unrestrict_primary_key
You can check the rules in the Sequel documentation.

Ruby BCrypt hash comparison

I'm trying to implement what seems like a very simple authentication approach using Sinatra and BCrypt but clearly I'm missing something...
Users are preassigned a temporary password which is stored in plaintext in the db.
I authenticate against the temp password and then create both a salt and password_hash and write them as strings to the db (mongo in this case).
To authenticate I fetch the salt from the db and user password to compare.
post "/password_reset" do
user = User.first(:email => params[:email], :temp_password => params[:temp_password])
if dealer != nil then
password_salt = BCrypt::Engine.generate_salt
password_hash = BCrypt::Engine.hash_secret(params[:password], password_salt)
user.set(:password_hash => password_hash)
user.set(:password_salt => password_salt)
end
end
post "/auth" do
#user = User.first(:email => params[:email])
#user_hash = BCrypt::Password.new(#user.password_hash) #because the password_hash is stored in the db as a string, I cast it as a BCrypt::Password for comparison
if #user_hash == BCrypt::Engine.hash_secret(params[:password], #user.password_salt.to_s) then
auth = true
else
auth = false
end
end
The value returned by BCrypt::Engine.hash_secret(params[:password], password_salt) is different than what is stored in the db (both are of class BCrypt::Password, but they don't match).
What am I missing here? Many thanks in advance for any insight!
Marc
BCrypt::Password is a subclass of String, and it overrides the == method to make checking passwords easier. When you do
if #user_hash == BCrypt::Engine.hash_secret(params[:password], #user.password_salt.to_s)
you end up performing the hash twice, and so they don’t match. If you compared directly with #user.password_hash rather than using BCrypt::Password.new you should see that they match.
The more “correct” way to use bcrypt-ruby for passwords is to not use the Engine class at all, just the Password class. You don’t need to manage the salt yourself, bcrypt takes care of that and includes it in the password hash string:
password_salt = BCrypt::Engine.generate_salt
password_hash = BCrypt::Engine.hash_secret("s3kr1t!", password_salt)
puts password_salt
puts password_hash
produces something like this:
$2a$10$4H0VpZjyQO9SoAGdfEB5j.
$2a$10$4H0VpZjyQO9SoAGdfEB5j.oanIOc4zp3jsdTra02SkdmhAVpGK8Z6
You’ll get something slightly different if you run it, since a different salt will be generated, but you can see that the password hash includes the salt.
In your case, you want something like this:
post "/password_reset" do
user = User.first(:email => params[:email], :temp_password => params[:temp_password])
if dealer != nil then
password_hash = BCrypt::Password.create(params[:password])
user.set(:password_hash => password_hash) # no need to store the salt separately in the database
end
end
post "/auth" do
#user = User.first(:email => params[:email])
#user_hash = BCrypt::Password.new(#user.password_hash)
if #user_hash == params[:password] then # overridden == method performs hashing for us
auth = true
else
auth = false
end
end

Resources