Norse
Norse

Reputation: 5757

Case insensitive usernames and password salts

I have a login system

where, for example, Joe can login with the username joe without any problems.

Currently, I am using a password encryption that uses the username as a salt. This is creating issues for logging in. For example,

SELECT * FROM `users` WHERE LOWER(`username`) = LOWER(`:username`)
$stmt->bindValue(':username', $_POST['user']);

This works fine. THe trouble involves the password:

SELECT * FROM `users` WHERE LOWER(`username`) = LOWER(`:username`)
   AND `password` = :password
$stmt->bindValue(':username', $_POST['user']);
$stmt->bindValue(':password', encrypt($_POST['password'], $_POST['user'])); //encrypt(password, salt)

As you can see, the password encryption wouldn't check with the database because the user has logged in with joe instead of Joe

Is there a workaround to this, or should I use something else as a salt? If so, what should I use as a salt? This is my encrypt function:

function encrypt($password, $salt) {
    return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($salt), $password, MCRYPT_MODE_CBC, md5(md5($salt))));
}

Upvotes: 0

Views: 1291

Answers (4)

Ryan Chouinard
Ryan Chouinard

Reputation: 2076

There are a couple issues here:

  1. The case-sensitivity of your salt
  2. Your method of password "encryption"

The first issue is what you've asked about, so let's take a look at that.

So your passwords are salted with the user name, and you expect to be able to use the username the user entered during login as the salt. This will work, if you settle on a normalization scheme for your salt. Instead of using it as-entered, convert it to lowercase or uppercase before using it in your function.

The other option is to perform two queries to the database: one to pull the username as it's stored, and the second to actually check the password. This isn't optimal, but it would really be the least of your troubles.

Is there a workaround to this, or should I use something else as a salt? If so, what should I use as a salt?

Since you asked, let's look at the second issue: your method of password "encryption."

Trying to invent your own password hashing/encryption scheme is never a good idea, for a number of reasons. Your best option is to use an already widely accepted password hashing scheme, such as bcrypt or some derivative of PBKDF2. These are great because they both include both salting and key stretching. They are designed to be slow (relatively, anyway) so brute-forcing these types of hashes is made very computationally expensive.

Using one of the aforementioned password hashing schemes will solve your first problem as well, as these schemes use built-in per-user salts. Depending on how you choose to implement these schemes, you will not (should not) need to provide your own salt. The top libraries will generate salts for you.

Try taking a look at these libraries for password hashing:

Upvotes: 2

Jon
Jon

Reputation: 437804

First off, what's the issue with using strtolower($_POST['user']) as the salt? It looks like it would work.

Now in general, salts should be unpredictable. Usernames aren't really that unpredictable, so although this won't be the end of the world your users' security will be better if you replace the username with a randomly generated string of modest length (it need not be crypto-strength random, just unpredictable).

But the biggest issue here is the use of MD5, which has been considered unsafe for some time. Security would improve if you switched to some other hash function; SHA-1 is not a particularly bad choice (it does have an undesirable property: it's fast to calculate), but the best fit for such applications are hash functions with a variable load factor such as bcrypt.

Upvotes: 2

G-Nugget
G-Nugget

Reputation: 8846

The problem you're having is one reason that username's shouldn't be used as the salt. A better way to hash passwords is with crypt(). You just need to generate a random salt to use with it. The string it returns contains the hash and the hashing algorithm so you can change the algorithm or difficulty if you want to without having to rewrite code. As long as your hash is 7-bit safe, the whole string will be, so you don't need to base64_encode() anything.

Upvotes: 1

SomeKittens
SomeKittens

Reputation: 39522

Skip the SQL lower and use PHP's strtolower(). That way your usernames will be consistent across the board.

Upvotes: 1

Related Questions