One minute
Authenticate By for preventing timing-based enum attacks ⏳
-
Add
authenticate_by
when usinghas_secure_password
.authenticate_by
is intended to replace code like the following, which returns early when a user with a matching email is not found:User.find_by(email: "...")&.authenticate("...")
Such code is vulnerable to timing-based enumeration attacks, wherein an attacker can determine if a user account with a given email exists. After confirming that an account exists, the attacker can try passwords associated with that email address from other leaked databases, in case the user re-used a password across multiple sites (a common practice). Additionally, knowing an account email address allows the attacker to attempt a targeted phishing (“spear phishing”) attack.
authenticate_by
addresses the vulnerability by taking the same amount of time regardless of whether a user with a matching email is found:User.authenticate_by(email: "...", password: "...")
Code snippet 📌
# Version: Rails 7.0 RC1
class User < ActiveRecord::Base
has_secure_password
end
User.create(name: "Rishi Pithadiya", email: "rishi@example.com", password: "abc123!@#")
User.authenticate_by(email: "rishi@example.com", password: "abc123").name
❯ "Rishi Pithadiya"
User.authenticate_by(email: "rishi@example.com", password: "wrong")
❯ nil
User.authenticate_by(email: "wrong@example.com", password: "abc123")
❯ nil
User.authenticate_by(email: "rishi@example.com")
❯ ArgumentError
User.authenticate_by(password: "abc123!@#")
❯ ArgumentError