Silent Trimming Of Passwords Longer Than 72 Characters
Introduction
The bcrypt
gem is a widely used password hashing library in Ruby, designed to provide secure password storage and verification. However, a critical vulnerability has been discovered in the gem, which can lead to unexpected behavior and potential security breaches. In this article, we will delve into the details of this vulnerability, its implications, and suggest a fix to ensure the security of password storage and verification.
The Vulnerability
The bcrypt
gem silently trims input strings longer than 72 characters during password hashing, which can lead to unexpected behavior. Specifically, when a string longer than 72 characters is provided, BCrypt::Password.create
truncates the input to 72 characters without warning, allowing different inputs to produce the same hash. This can potentially enable password verification bypasses if an attacker uses a different string that matches the first 72 characters of the original input.
Steps to Reproduce
To reproduce this vulnerability, follow these steps:
- Install the bcrypt gem using
gem install bcrypt
. - Run the following Ruby script:
require 'bcrypt'
cost = 12
userid = "b91fa9b4-69f1-4779-8d45-73f8653057f3" # 36 characters
username = "my.very.long.username.with.more.characters@kondukto.io" # 47 characters
password = "randomStrongPassword" # 20 characters
input = userid + username + password # Total: 103 characters
begin
my_password = BCrypt::Password.create(input, cost: cost)
puts "Hashed password: #{my_password}"
# Validate the correct password
is_valid = BCrypt::Password.new(my_password) == input
puts "Correct password validation: #{is_valid ? 'Success' : 'Failure'}"
# Validate with a different password
wrong_password = "AAAAAAAAAAAAAAAAAAAA"
bypass_input = userid + username + wrong_password # Also 103 characters
is_valid = BCrypt::Password.new(my_password) == bypass_input
puts "Bypass password validation: #{is_valid ? 'Success' : 'Failure'}"
rescue BCrypt::Errors::InvalidCost, BCrypt::Errors::InvalidSalt => e
puts "Error creating password: #{e.message}"
end
- Observe the output:
└> ruby main.rb
Hashed password 1: $2a$12$nefsa21AluV1BF2EXx6Y4.u6ZV4KT3c1ZWXLIOpWV9KZZ2Y1lGQmO
Password validation: Success
Password validation: Success
Expected Behavior
The bcrypt gem should either:
- Raise an error when an input string exceeds 72 characters, indicating that the input is too long.
- Explicitly document the 72-character limit and warn about truncation in the gem's documentation.
Actual Behavior
The bcrypt gem silently trims the input string to 72 characters. Both input
(103 characters) and bypass_input
(103 characters, differing after 72 characters) successfully against the same hash, indicating that only the first 72 characters are considered. No warning or error is raised about the truncation.
Suggested Fix
To fix this vulnerability, add a check in BCrypt::Password.create
to raise an error (e.g., BCrypt::Errors::InputTooLong
) if the input exceeds 72 characters. Alternatively, log a warning when truncation occurs to alert developers. Update the gem's documentation to clearly state the 72-character limit and the truncation behavior.
Environment
- Ruby version: ruby 3.2.3 (2024-01-18 revision 52bb2ac0a6) [x86_64-linux-gnu]
- bcrypt version: 3.1.13
- Operating System: Ubuntu 24.04.2 LTS
References
- https://www.usenix.org/legacy/events/usenix99/provos/provos.pdf
- https://en.wikipedia.org/wiki/Bcrypt
- https://kondukto.io/blog/okta-vulnerability-bcrypt-auth
Q: What is the silent trimming vulnerability in the bcrypt gem?
A: The silent trimming vulnerability in the bcrypt gem occurs when input strings longer than 72 characters are silently truncated during password hashing. This can lead to unexpected behavior and potential security breaches, as different inputs can produce the same hash.
Q: How does the silent trimming vulnerability affect password verification?
A: The silent trimming vulnerability can enable password verification bypasses, as an attacker can use a different string that matches the first 72 characters of the original input. This can lead to successful password validation even if the input is different.
Q: What are the implications of the silent trimming vulnerability?
A: The silent trimming vulnerability can have significant implications for password security, including:
- Password verification bypasses: An attacker can use a different string that matches the first 72 characters of the original input to successfully validate a password.
- Security breaches: The silent trimming vulnerability can lead to security breaches, as attackers can exploit the vulnerability to gain unauthorized access to sensitive information.
- Loss of trust: The silent trimming vulnerability can erode trust in the bcrypt gem and other password hashing libraries, as developers may question the security of their password storage and verification mechanisms.
Q: How can I reproduce the silent trimming vulnerability?
A: To reproduce the silent trimming vulnerability, follow these steps:
- Install the bcrypt gem using
gem install bcrypt
. - Run the following Ruby script:
require 'bcrypt'
cost = 12
userid = "b91fa9b4-69f1-4779-8d45-73f8653057f3" # 36 characters
username = "my.very.long.username.with.more.characters@kondukto.io" # 47 characters
password = "randomStrongPassword" # 20 characters
input = userid + username + password # Total: 103 characters
begin
my_password = BCrypt::Password.create(input, cost: cost)
puts "Hashed password: #{my_password}"
# Validate the correct password
is_valid = BCrypt::Password.new(my_password) == input
puts "Correct password validation: #{is_valid ? 'Success' : 'Failure'}"
# Validate with a different password
wrong_password = "AAAAAAAAAAAAAAAAAAAA"
bypass_input = userid + username + wrong_password # Also 103 characters
is_valid = BCrypt::Password.new(my_password) == bypass_input
puts "Bypass password validation: #{is_valid ? 'Success' : 'Failure'}"
rescue BCrypt::Errors::InvalidCost, BCrypt::Errors::InvalidSalt => e
puts "Error creating password: #{e.message}"
end
- Observe the output:
└> ruby main.rb
Hashed password 1: $2a$12$nefsa21AluV1BF2EXx6Y4.u6ZV4KT3c1ZWXLIOpWV9KZZ2Y1lGQmO
Password validation: Success
Password validation: Success
Q: What the expected behavior of the bcrypt gem?
A: The expected behavior of the bcrypt gem is to either:
- Raise an error when an input string exceeds 72 characters, indicating that the input is too long.
- Explicitly document the 72-character limit and warn about truncation in the gem's documentation.
Q: What is the actual behavior of the bcrypt gem?
A: The actual behavior of the bcrypt gem is to silently trim the input string to 72 characters. Both input
(103 characters) and bypass_input
(103 characters, differing after 72 characters) successfully validate against the same hash, indicating that only the first 72 characters are considered. No warning or error is raised about the truncation.
Q: How can I fix the silent trimming vulnerability?
A: To fix the silent trimming vulnerability, add a check in BCrypt::Password.create
to raise an error (e.g., BCrypt::Errors::InputTooLong
) if the input exceeds 72 characters. Alternatively, log a warning when truncation occurs to alert developers. Update the gem's documentation to clearly state the 72-character limit and the truncation behavior.