I have a question regarding using Login Names to protect passwords.
You salt the Login Name with a shared salt and then hash it with BCrypt.
You then take the original plain text Login Name and use it as a key to encrypt the password with AES. The result is then salted with a unique salt and finally hashed with BCrypt.
The user's Display Name is set to their User ID (integer), as opposed to their Login Name, when the account is created. The user can change it later, except it can not match anyone else's Display Name or too closely match a case-insensitive comparison to their Login Name.
My question is, if the database were compromised, would this make it significantly harder to recover the passwords than storing plaintext usernames and uniquely salted and BRcypted passwords?
This procedure appears to be very complicated. Actually you are adding a server side secret to the stored hashes (your procedure to hash passwords). That will indeed increase security, as long as the procedure stays secret. In other words, if the attacker has only read access to the database (SQL-injection) you have an advantage, as soon as he has got privileges on the server and knows the code, there is no advantage at all.
There is a much easier and safer way to get this advantage though. Just calculate a BCrypt hash with a random salt (most implementations will do that anyway), then encrypt this hash-value with a server-side key (AES for example). The key should not be derrived form other parameters, instead use a long and random enough key.
I tried to explain the reasons in my tutorial about safely storing passwords , maybe you want to have a look at it.
Edit:
Well i understand now, that you want to handle the login-name like a second password, and will not store it plaintext, instead you store only a BCrypt hash of it with a global "salt".
Lets assume that you are willing to spend 1 second of CPU time for password hashing. In your scheme you would have to split it to half a second for hashing the login-name and half a second for the password. An attacker has to brute-force the login-name first and the password in a second step.
That means you spend half a second for a very weak password (login-name), then half a second for the normal password. Compared to investing 1 second for the normal (hopefully strong) password, you are probably decreasing security, by all means i cannot see any advantage.
The authentication code would look something like this.
public static LoginResult TryLogin(string loginName, string pwd)
{
string loginHash = BCrypt.Net.BCrypt.HashPassword(loginName, SHARED_SALT);
WidgetDataContext dc = new WidgetDataContext();
var record = (from rec in dc.usp_GetUserByLoginName(loginHash)
select rec).SingleOrDefault();
if (record == null)
return new LoginResult(null, "Invalid Login Name/Password");
if (record.FailedLoginCount >= MAX_CONSECUTIVE_LOGIN_FAILURES)
return new LoginResult(null, "You have exceeded your maximum number of Login failures. Your account is locked.");
if (record.Locked) // In case account is locked for another reason
return new LoginResult(null, "Your Account is locked.");
pwd = EncryptionServices.Encrypt(pwd, loginName);
pwd = BCrypt.Net.BCrypt.HashPassword(pwd, record.Salt);
if (pwd == record.Password)
{
record.FailedLoginCount = 0;
dc.SubmitChanges();
return new LoginResult(record.UserId, "Login Successful");
}
record.FailedLoginCount++;
dc.SubmitChanges();
return new LoginResult(null, "Invalid Login Name/Password");
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.