In this exponentially growing digital world, everybody is worried about one thing.
Yes, it is SECURITY.
The need for a security is inevitable in today’s cyber ecosystem of phishing and breaches.
The fundamental mechanism used by most websites
to achieve security is Authentication
.
In simple terms, the user/customer needs to log in using a username/email
and password to access various contents of the website.
Before:
Let’s say, we have a Customer
model
with the email
attribute.
It has a record with the email "richard_roe@example.com"
.
We can create this customer record as follows:
Customer.create(name: "Richard Roe", email: "richard_roe@example.com", password: "password123")
Note: We are using the following code snippet to authenticate the customer. The following code returns early when a customer with a matching email is not present.
Customer.find_by(email: "richard_roe@example.com")&.authenticate("password123")
Now, the above code snippet is vulnerable to timing-based enumeration attacks, wherein an attacker can determine if a customer with the given email exists or not.
It is a common human tendency to re-use the same password across multiple websites instead of using password manager applications.
After confirming that an account exists in the database, the attacker can try a password associated with that same email address from other leaked databases over the world wide web. If an account email address is known, it allows the attacker to attempt a targeted brute force or phishing (“spear-phishing”) attack as well.
After:
Rails introduces a new class method authenticate_by
.
Customer.authenticate_by(email: "richard_roe@example.com", password: "password123")
authenticate_by will cryptographically digest the given password attributes, which helps mitigate timing-based enumeration attacks. This method finds a record using the non-password attributes and then authenticates that record using the password attributes.
It returns the record,
if authentication succeeds;
otherwise, it returns nil
.
class Customer < ActiveRecord::Base
has_secure_password
end
Customer.create(name: "Richard Roe", email: "richard_roe@example.com", password: "password123")
Customer.authenticate_by(email: "richard_roe@example.com", password: "password123").name
# => "Richard Roe"
Customer.authenticate_by(email: "richard_roe@example.com", password: "invalid_password")
# => nil
Customer.authenticate_by(email: "invalid@example.com", password: "password123")
# => nil
This method raises an ArgumentError
if the set of attributes doesn’t contain
at least one password attribute and one non-password attribute.
Customer.authenticate_by(email: "richard_roe@example.com")
# => ArgumentError
Customer.authenticate_by(password: "password123")
# => ArgumentError
Check out the PR (including #43779, #43958, and #43997) that made this happen!