开发者

Hash on Unicode Password

开发者 https://www.devze.com 2023-01-25 14:59 出处:网络
I\'m writing a password salt/hash procedure for my .NET application, largely following the guide of this article:

I'm writing a password salt/hash procedure for my .NET application, largely following the guide of this article: http://www.aspheute.com/english/20040105.asp

Basically the code for computing the salted hash is this:

public string ComputeSaltedHash(string password, byte[] salt) {

    // Get password ASCII text as bytes:
    byte[] passwordBytes = System.Text.Encoding.ASCII.GetByt开发者_StackOverflowes(password);

    // Append the two arrays
    byte[] toHash = new byte[passwordBytes.Length + salt.Length];
    Array.Copy(passwordBytes, 0, toHash, 0, passwordBytes.Length);
    Array.Copy(salt, 0, toHash, passwordBytes.Length, salt.Length);

    byte[] computedHash = SHA1.Create().ComputeHash(toHash);

    // Return as an ASCII string
    return System.Text.Encoding.ASCII.GetString(computedHash);
}

However, I want to allow allow users to use Unicode chars in their password, if they like. (This seems like a good idea; can anyone think of a reason it's not?)

However, I don't know a ton about how Unicode works, and I'm worried if I just change both references of System.Text.Encoding.ASCII to System.Text.Encoding.Unicode, the hash algorithm might produce some byte combinations that don't form valid Unicode chars and the GetString call will freak out.

Is this a valid concern, or will it be OK?


You shouldn't be using any normal encoding to convert from arbitrary binary data back to a string. It's not encoded text - it's just a sequence of bytes. Don't try to interpret it as if it were "normal" text. Whether the original password contains any non-ASCII characters is irrelevant to this - your current code is broken. (I would treat the linked article with a large dose of suspicion simply on that basis.)

I would suggest:

  • Use Encoding.UTF8 to get the bytes from the password. That will allow the password to contain any unicode character. Encoding.Unicode would be fine here too.
  • Use Convert.ToBase64String to convert from the computed hash back to text. Base64 is specifically designed to represent opaque binary data in text within the ASCII character set.


It's enough to change the first reference to Unicode or UTF-8. You may want to normalize the input, however, to account for various ways of entering accents and the like.


I like Jon solution better but another option is to simply store the raw hexadecimal as a string. Replace your last line with:

return BitConverter.ToString(computedHash)

One thing you may want to consider is password strengthening.
SHA1 is very fast, sometimes too fast. A system will be able to compute couple million hashes per second. The speed allows an attacker to try a common word dictionary attack (including variantions in capitalization) and number extensions. The speed of SHA1 allows a wide dictionary space to be completed in a reasonable amount of time breaking most user's passwords.

A method to strengthen the passwords is to hash it multiple times this increases the CPU requirements of the hash. Take the output of the SHA1 hash and pass it as the input for a second round. Do that at least 1000 times. This slows down hash computation for both you and an attacker. For your users it delays access by a trivial amount of time; the routine will return in 0.01 seconds instead of 0.0001 seconds. However to a brute force attack you have increased execution time by a factor of 1000.

You can roll your own but .net framework exposes a class to do just that: System.Security.Cryptography.Rfc2898DeriveBytes

RFC2898 uses SHA1 algorith and accepts plain text, salt, and number of iterations. It can output a variable length key.

http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx

0

精彩评论

暂无评论...
验证码 换一张
取 消