Ruby BCrypt hash comparison - ruby

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

Related

BCrypt::Password Ruby

I'm currently working on Ruby and I can't get, what is the difference between
def create_hash_digest(password)
BCrypt::Password.create(password)
end
def verify_hash_digest(password)
BCrypt::Password.new(password)
end
This sample is taken from Udemy course on complete Ruby on Rails
#create is used to Hash a secret, returning a BCrypt::Password instance i.e encryption
#new is used to decrypt BCrypt::Password instance with the data from a stored hash.
include BCrypt
# hash a user's password
#password = Password.create("my grand secret")
#password #=> "$2a$12$C5.FIvVDS9W4AYZ/Ib37YuWd/7ozp1UaMhU28UKrfSxp2oDchbi3K"
# store it safely #user.update_attribute(:password, #password)
# read it back
#user.reload!
#db_password = Password.new(#user.password)
# compare it after retrieval
#db_password == "my grand secret" #=> true
#db_password == "a paltry guess" #=> false
#create converts your password into a hash:
'my_password' ~> '$2a$12$C5.FIvVDS9W4AYZ/Ib37...'
while #new converts it back.

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.

Ruby BCrypt hash comparison not working

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.

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

net-ldap create user with password

I am trying to create an AD account with a password already set using the net-ldap gem. I am able to connect to the server fine. And I am also able to add a new user without passing the :unicodepwd attribute however when the new user is created there is no password set. When I do pass that attribute the user is not created and it fails with error code 53 and the following message Unwilling to perform. I also get the same error if I try to replace the password of the user after I have created it. I've come across many potential answers but none of them have worked for me.
def initialize
#client = Net::LDAP.new
#client.host = server_ip
#client.base = base
#client.port = 389
#client.auth(username, password)
if #client.bind
puts "Connected"
add("TEST", "JEST", "testjest")
else
puts "Not Connected"
display_error
end
end
def add(first_name, last_name, username)
dn = dn_value
attrs = {
:objectclass => ["top", "person", "organizationalPerson", "user"],
:cn => fullname(first_name, last_name),
:sn => last_name.capitalize,
:givenname => first_name.capitalize,
:displayname => fullname(first_name, last_name),
:name => fullname(first_name, last_name),
:samaccountname => username,
:unicodePwd => '"password"'.encode("utf-16")
}
#client.add(:dn => dn, :attributes => attrs)
if #client.get_operation_result.code != 0
puts "Failed to add user #{fullname(first_name, last_name)}"
display_error
else
puts "Added user #{fullname(first_name, last_name)}"
end
end
How would I set a password for the user when I create the user and not have to access it through the gui in order to update the password? Any help is appreciated
Thanks
UPDATE
I was able to get this to work once I encoded the string in a different way and connected to the SSL port 636 rather than default port 389. Using encode was the issue, seems like it was incorrectly encoding the password.
This is my new connection
#client = Net::LDAP.new
#client.host = server_ip
#client.base = base
#client.port = 636
#client.encryption(:method => :simple_tls)
#client.auth(username, password)
And the method which i used to encode the password
def encode_passwd(string)
newstring = ""
string = "\"" + string + "\""
string.split("").each do |c|
newstring = "#{newstring}#{c}\000"
end
return newstring
end
Hope this helps someone in the future
The Net::LDAP::Password.generate does not work with ActiveDirectory. The :unicodePwd LDAP-Entry-Attribute (speaking ruby-gem net-ldap parlance), you
have to encode it like this
unicodepwd = "\"#{plain_text_password}\"".encode(Encoding::UTF_16LE).force_encoding(Encoding::ASCII_8BIT)
See details about the encoding here: https://msdn.microsoft.com/en-us/library/cc223248.aspx
I just found out there is already a password generation function included in Net::LDAP !
Net::LDAP::Password.generate(:md5, 'yourPlaintextPass')
Documentation here

Resources