
Introduction
Almost every website nowadays needs to maintain a list of users and passwords. Many multi-user applications require a way to authenticate users, and passwords seem like a natural.
You don�t necessarily have to provide your own username/password authentication solution. Alternatives include using Active Directory (if you are in a Microsoft domain), Microsoft Passport, LDAP (Lightweight Directory Access Protocol) for non-Microsoft environments, and the membership framework provided in .NET 2.0. All four alternatives have their place, but if you want to understand the logic behind storing passwords and understand how to correctly implement a password management scheme, this article is for you.
This article assumes a web application � slightly different rules apply to a distributed multi-user application. For distributed multi-user applications, the authentication exchange follows slightly different rules.
The trivial approach � just store it!
The simplest approach to manage user names and passwords is to store everything in plaintext (no encryption or scrambling) in a file or database. The result would be something like this:
|
User ID |
User Name |
Password |
|
101 |
Bob |
Snake |
|
102 |
David |
Rainbow6 |
|
103 |
George |
DarkTower |
|
104 |
Eve |
Snake |
Advantages of storing passwords �in the clear�
- Authenticating (checking that the username and password pair matches the pair in the table) is very simple � just compare the strings!
- Forgotten passwords can be retrieved � the password is easily accessible, given the user name.
Disadvantages of storing passwords �in the clear�
- First, anyone with access to the file (or able to
SELECT from the table) gains immediate access to all passwords! An employee with legitimate access to the file might print the file or email out the information, and Voila! all the passwords are compromised.
But SQL 2005 supports encryption � isn�t that good enough? No, SQL encryption is not good enough. The built-in encryption only protects the information on disk. If a user is allowed to access the data (perform a SELECT), SQL will automatically decrypt the information. If your web application is allowed to access the data (and how else would you compare the user name and password?) then a hacker hacking your application can access the data as well, gaining the same access as your web application.
- The second problem is that during the authentication exchange, the password is visible on the network. Unless secure communication is used throughout, the password can be seen while traveling on the network. For example, even if the web application uses SSL to submit the password, the password is still visible when the web application server
SELECTs the information from the remote database. The results of the query are transferred unencrypted over the network.
Is storing password �in the clear� an acceptable solution?
- If your data store is encrypted (using SQL Server 2005, for example),
- and your internal communication network is highly secure (uses only IP-SEC or a VPN tunnel for communication between servers),
- and you only use secure communication between the application client and the application server,
- and you trust all employees with database access to never make mistakes (such as printing the password information or storing to file),
- and you are sure nobody else has physical access to any of the servers used.
Then, yes, storing passwords �in the clear� is fine.
Encrypting passwords � a little more secure
A better approach for storing passwords (and the only viable alternative if users need to be able to recover passwords) is scrambling the passwords before storing them.
This approach relies on having a secret. The secret is either the scrambling algorithm, or the key used in conjunction with a modern encryption algorithm.
Scrambling (or encrypting) passwords is a reversible operation. The secret is used to garble the password, and the same secret can be used to retrieve the original password. When a user supplies a password, the stored password is de-scrambled using the secret, and the passwords are compared. An alternative approach is to scramble the provided password using the secret and compare the two garbled versions � a match indicates the provided password was good.
If a user needs to retrieve a password, the stored password is de-scrambled and provided to the user (usually via email).
|
User ID |
User Name |
Password |
|
101 |
Bob |
k468dD8F |
|
102 |
David |
56lkV#p6 |
|
103 |
George |
8Fk4lVQ0 |
|
104 |
Eve |
k468dD8F |
Advantages of scrambling using a secret
- A forgotten or lost password can be retrieved.
- Only a single secret needs to be stored securely (either an algorithm or a key).
- For multi-user distributed applications, when using encryption, either the clear text password needs to be communicated (to be checked), or the secret must be communicated to perform the authentication on the front end.
Disadvantages of scrambling passwords
- If the secret is compromised, all passwords are compromised. If somebody has access to the secret and to the password store, all passwords can be unscrambled!
- Access to the password store alone is sufficient to provide information about the passwords. Since all passwords are scrambled using the same algorithm, if two users share the same scrambled password, they must have the same password as well. Crafty hackers with access to the password store can create users with known passwords and check for other users with the same password. This type of attack is a variation of the �known plaintext� attack. Attacks such as this can be stopped using a SALT (see below).
- When using a block cipher, the password length must be stored as part of the scrambled password. The length must be stored because block ciphers always produce a fixed-size block of scrambled text. If the password length is not scrambled (for example, if stored as a column in a table), the information is very useful to password crackers. Knowing the exact length of the password makes it much easier to try to guess a password.
Is this an acceptable password storing solution?
If lost password retrieval is a must, then yes, this is the only acceptable solution. A few guidelines though:
- Store your secret in a secure place. Hard-coding the secret in the application code is not a good solution. In my opinion, storing the secret in a file (even if it is the web.config file) is a terrible idea. If you must use a secret, store it in a database (limiting access by requiring an authenticated connection) or in an access-restricted registry key.
- Use a good cryptographic algorithm (examples below), don�t create your own or use a trivial scrambling algorithm. Unless you know exactly what you are doing, your own algorithm might be very easy to crack.
- Use a SALT (see below) to prevent two users with the same password from having the same scrambled password.
- Encryption output is binary, and must be encoded if stored as text. If storing binary data is OK, no encoding is necessary, but if the encrypted passwords are to be stored as text, consider using RADIX64 to convert binary information to text. RADIX64 uses a character for every 6 bits, expanding the output by 33%.
Storing hashed passwords � the one-way solution
A cryptographic hash is also known as a �one-way function�. A hash function takes input of any length, and produces a unique output of constant length. For example, if we hash a password (of any length) with the MD5 cryptographic hash, the result would be a 128 bit number that uniquely corresponds to the password. Cryptographic hashes work on more than passwords � if the cryptographic hash of two files is identical, then the two files are identical as well.
In recent years, as computing power increased, some cryptographic hash functions are no longer recommended for use (MD4, MD5, SHA1). In my opinion, if used just for hashing passwords, you are probably OK. As for me, I modified my code to use SHA2.
When storing hashed passwords, the password is hashed (run through the hashing algorithm) and the resulting hash is stored instead of the password. To compare passwords, just hash the given password using the same hash function and compare the results. If the hashes are the same, the passwords match.
The beauty of a one way function is that there is no way to compute the password based on the hash. Hashed passwords are not immune to brute force attacks � given a dictionary and the password hash, a hacker can compute the hashes of all the words in the dictionary, compare the words with the password hash, and discover the password. This is where strong passwords (containing letters, numbers, and special characters) help us defend against brute-force attacks.
Advantages of storing hashed passwords
- The original �clear text� password is never stored. Even if the password store is compromised, only the hashes become public.
- The password length is not stored and cannot be estimated, making password cracking that much harder.
- There is no need for a secret as none is used to hash the password.
- For multi-user distributed applications, the password hash can be used for authentication. When using encryption, either the clear text password needs to be communicated (to be checked), or the secret must be communicated to perform the authentication on the front end.
Disadvantages of storing hashed passwords
- Lost passwords cannot be recovered (except using brute-force methods). A new password must be created and communicated to the user.
- Like encrypted passwords, if a SALT (see below) is not used, users with the same password will have the same password hash.
Is this an acceptable password storing solution?
Yes, but please follow these guidelines:
- Use a good cryptographic hash. MD5 is no longer recommended, and neither is SHA1. SHA2 is the current favorite. If you must use MD5 or SHA1, use a stronger salting algorithm.
- Use a SALT (see below) to prevent two users with the same password from having the same scrambled password.
- Hash output is binary, and must be encoded if stored as text. If storing binary data is OK, no encoding is necessary, but if the hashed passwords are to be stored as text, consider using RADIX64 to convert binary information to text. RADIX64 uses a character for every 6 bits, expanding the output by 33%.
Storing password length � why and how
Why do I need to store the password length?
Password length is only needed when encrypting and decrypting the password using a block cipher. Many block ciphers have a block size of 64 bits - anything encrypted will end up with a multiple of 8 bytes for size. Encrypting a 12 byte password would result in a 16 byte output. When the 16 bytes are decrypted, the result would be a 16 byte string with garbage for the last 4 bytes. If the password length is stored, the extra �padding� can be trimmed when decrypting the password. Without the trimming, any na�ve attempts to compare the strings will fail � the 12 character password is not the same as the 16 character decrypted password. Also, attempting to compare the encrypted password to check for a match might fail because the extra 4 bytes of padding might be different from encryption to encryption.
Hashing does not require a password length � the hash of a 12 character password using SHA2 will always result in a 320 bit output, and hashing the same password will always result in the same 320 bit output.
When using a scrambling algorithm such as ROT13 (which I do not recommend), the scrambled password has the same length as the original password. Anyone with access to the password store can then obtain the length of the password. If you decide to use a scrambling algorithm or a stream cipher (as opposed to a block cipher), please make sure to pad the output to hide the password length.
How can I store the password length?
One solution is to store the password length as a column in your table. The problem with this approach is that if a hacker gains access to the information, the length of a password is very useful when attempting a brute-force attack. Knowing that a password has only 5 letters allows the hacker to limit the number of password guesses needed to crack the password.
A better solution is to store the password length as part of the encrypted string. The password length can be pre-pended to the password string (for example, as the first two characters). When the password is decrypted, the first two characters are used to reconstruct the length, and the password can be safely trimmed. Storing the length encrypted with the password makes sure no-one can access the password length without knowing the secret used to encrypt the password.
Example � Storing length and trimming passwords
Storing a message with the length:
byte[] length = new byte[4];
length[0] = (byte)(message.Length & 0xFF);
length[1] = (byte)((message.Length >> 8) & 0xFF);
length[2] = (byte)((message.Length >> 16) & 0xFF);
length[3] = (byte)((message.Length >> 24) & 0xFF);
csEncrypt.Write(length, 0, 4);
csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
Retrieving the message from a binary array:
byte[] fromEncrypt = new byte[encrypted.Length-4];
UTF8Encoding textConverter = new UTF8Encoding();
byte[] length = new byte[4];
csDecrypt.Read(length, 0, 4);
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
int len = (int)length[0] | (length[1] << 8) |
(length[2] << 16) | (length[3] << 24);
return textConverter.GetString(fromEncrypt).Substring(0, len);
Salting � everything tastes better with a little salt
Hashing (or encrypting) the same password for two users results in the same output. The repeated information can be used maliciously to obtain passwords. If I am a user with access to the password store, I can set my own password to a dictionary word, and then scan the password store for somebody else with the same hashed or encrypted password. If I find a match, I cracked that user�s password.
Simple salting � use an additional piece of data
To prevent the above problem, we can inject some variation into the hashing (or encrypting scheme). For example: if we prefix the password with the user name, the hash or encrypted output will no longer match.
User: Bob
Password: Snake
Hashed password: hash(�Snake�) -> k468dD8F
User: Eve
Password: Snake
Hashed password: hash(�Snake�) -> k468dD8F
Obviously, Bob and Eve have the same password. Even worse, if a hacker obtains our password store, the hacker can pre-compute the hashes for an entire dictionary and look for matches in our password store, greatly accelerating the cracking process.
If we throw the user name into the mix:
User: Bob
Password: Snake
Hashed password: hash(�Bob.Snake�) -> 4Fgja93Q
User: Eve
Password: Snake
Hashed password: hash(�Eve.Snake�) -> k468dD8F
Bob and Eve now have different password hashes. If a hacker gets hold of our password store, the hacker now needs to compute each password hash, specifically for each user. The hacker needs to pre-compute the dictionary hashes with the �Bob.� prefix for Bob and with the �Eve.� prefix for Eve � no free lunch here.
Advanced salting � use random information
Using a random salt significantly improves the strength of encrypting passwords, and makes brute-force cracking much more costly.
To use a random salt, compute a random number, and use the random number as a component when calculating the hash or when encrypting. Store the random number in a database column so the number can be available later when checking the password.
For example:
public static byte[] GenerateSALT()
{
byte[] data = new byte[6];
new System.Security.Cryptography.RNGCryptoServiceProvider().GetBytes(data);
return data;
}
When initially storing the password for Bob:
- Compute a random number (use a good cryptographic random generator like the ones in
System.Security.Cryptography).
- Add the random number to the password string.
- Compute the hash, or encrypt the resulting string.
- Store both the hash/encryption output and the random number in the password store.
When comparing passwords, follow the same algorithm:
- Obtain the random number from the password store.
- Add the random number to the password string.
- Compute the hash, or encrypt the resulting string.
- Compare the result with the stored hash or encrypted password, a match indicates a password match.
Using a random number is even better than using the user name. User names are not very random - they follow very strict rules. Random numbers (when coming from a good cryptographic random number generator) inject more randomness into the resulting hash or encrypted output. Note that if you decide to use the user name as a salt, the user name can never change. If the user name changes, the hash is no longer valid.
Encoding binary output as text
The output from hash functions and encryption algorithms is binary. To store the binary information as textual strings, the information can be encoded.
Two popular encoding schemes are UUENCODE (popular in the Unix world) and Base64 (popular everywhere). Base64 is the encoding scheme used to convert binary attachments for sending over SMTP email.
Example � encoding a binary array to a string
byte[] encrypted = msEncrypt.ToArray();
string b64 = Convert.ToBase64String(encrypted);
Example � decoding a string to a binary array
byte[] encrypted = Convert.FromBase64String(b64);
Scrambling algorithms and examples
The .NET framework (System.Security.Cryptography) comes with built-in support for several encryption algorithms:
- DES � old, somewhat obsolete.
- TripleDES � old, but still strong.
- RC2 � old, but still useful.
- Rijndael (AES) - modern.
Example - encoding a string:
UTF8Encoding textConverter = new UTF8Encoding();
RC2CryptoServiceProvider rc2CSP =
new RC2CryptoServiceProvider();
byte[] toEncrypt = textConverter.GetBytes(message);
ICryptoTransform encryptor =
rc2CSP.CreateEncryptor(ScrambleKey, ScrambleIV);
MemoryStream msEncrypt = new MemoryStream();
CryptoStream csEncrypt = new CryptoStream(msEncrypt,
encryptor, CryptoStreamMode.Write);
byte[] length = new byte[4];
length[0] = (byte)(message.Length & 0xFF);
length[1] = (byte)((message.Length >> 8) & 0xFF);
length[2] = (byte)((message.Length >> 16) & 0xFF);
length[3] = (byte)((message.Length >> 24) & 0xFF);
csEncrypt.Write(length, 0, 4);
csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
csEncrypt.FlushFinalBlock();
byte[] encrypted = msEncrypt.ToArray();
Example � decoding a string:
UTF8Encoding textConverter = new UTF8Encoding();
RC2CryptoServiceProvider rc2CSP = new RC2CryptoServiceProvider();
ICryptoTransform decryptor =
rc2CSP.CreateDecryptor(ScrambleKey, ScrambleIV);
MemoryStream msDecrypt = new MemoryStream(encrypted);
CryptoStream csDecrypt = new CryptoStream(msDecrypt,
decryptor, CryptoStreamMode.Read);
byte[] fromEncrypt = new byte[encrypted.Length-4];
byte[] length = new byte[4];
csDecrypt.Read(length, 0, 4);
csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
int len = (int)length[0] | (length[1] << 8) |
(length[2] << 16) | (length[3] << 24);
return textConverter.GetString(fromEncrypt).Substring(0, len);
Hashing algorithms and examples
The .NET framework (System.Security.Cryptography) comes with built-in support for several cryptographic hashes:
- MD5 � Broken, attempt to avoid.
- RIPEMD160 � Acceptable, never popular in the US.
- SHA1 � Acceptable, but a 160 bit output is considered too short.
- SHA256 � The recommended minimum.
- SHA384 (SHA2) - Recommended.
- SHA512 � Very good, but does not provide any additional protection compared to SHA384.
Example � hashing a string:
public byte[] EncryptPassword(string userName, string password,
int encryptionVersion, byte[] salt1, byte[] salt2)
{
string tmpPassword = null;
switch(encryptionVersion)
{
case 2:
tmpPassword = Convert.ToBase64String(salt1)
+ Convert.ToBase64String(salt2)
+ userName.ToLower() + password;
break;
case 1:
tmpPassword = userName.ToLower() + password;
break;
case 0:
default:
tmpPassword = password;
break;
}
UTF8Encoding textConverter = new UTF8Encoding();
byte[] passBytes = textConverter.GetBytes(tmpPassword);
if (encryptionVersion == 2)
return new SHA384Managed().ComputeHash(passBytes);
else
return new MD5CryptoServiceProvider().ComputeHash(passBytes);
}
Example � comparing two hashes for equality:
private bool PasswordsMatch(byte[] psswd1, byte[] psswd2)
{
try
{
for(int i = 0; i < psswd1.Length; i++)
{
if(psswd1[i] != psswd2[i])
return false;
}
return true;
}
catch(IndexOutOfRangeException)
{
return false;
}
}
Conclusion
By now, you have enough information to make informed decisions about storing passwords.
The simple guidelines are:
- If you need to retrieve passwords, use encryption.
- If you do not need to retrieve passwords, use hashes (more secure).
- Whatever you do, salt the passwords.
Further reading
- Handbook of Applied Cryptography (Amazon and Online) - Alfred J. Menezes, Paul C. van Oorschot, Scott A. Vanstone � this is the best cryptography book ever!
- Applied Cryptography: Protocols, Algorithms, and Source Code in C � Bruce Schneier (Amazon).
- Scramble Your Query Strings � gtamir.
| You must Sign In to use this message board. |
|
|
 |
|
|
 |
|
 |
<blockquote class="FQ"><div class="FQA">PetoG wrote:</div>ou didn't mention where is the best place to store a scramble key from scrambling algorithms.</blockquote> If you wanted to store the key (you do not have to), your .config file might be a good spot. Starting with the 2.0 framework, the configuration file can have encrypted sections. You can also put the key directly in the codebehind source (probably not as recommended) or you can store the key in a DB table and read on startup. All the above are acceptable, but I would recommend generating a random key on starting your web application and using the random key for the life of the application. Assuming you were not planning on people retaining links to URLs including the scrambled query strings, random keys are fine. See the above for the code to generate the random key.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Sometimes using a one-time salt is also a good measure. It involves the following:
The database has an extra field, namely, SALT. This field should be set to a new value each time the user tries to log in. This value should be cryptographically strong, I recommend setting it to NewGuid().
The client receives the following hidden fields:
SALT = DATABASE.SALT
The client fills in their information, however, a javascript function handles the login button and does the following before sending the data to the server:
SALTED = SHA1(SALT + SHA1(LOWERCASE(USER) + PASSWORD))
PASSWORD = ""
The javascript MUST hook the login button, instead of being explicitly tied to it, in case the user's browser does not have javascript.
The server then looks to check if the SALTED field is empty. If it is empty:
VALID = SHA1(LOWERCASE(USER) + PASSWORD) == DATABASE.HASH
Fyi, DATABASE.HASH is as defined in the article:
DATABASE.HASH = SHA1(LOWERCASE(USER) + PASSWORD)
Otherwise:
VALID = SALTED == SHA1(DATABASE.SALT + DATABASE.HASH)
This protects the password as it travels over the wire. The client is also protected from a replay attack occuring because the SALTED field will be different even though the username and password are the same.
Remember, just because you are behind SSL doesn't mean someone can't replay packets, although there will be a lot of determination involved.
An finally, you are never safe in the first place, certain memory scrubbing programs can get a password. And keyboard hooks foil even the best memory encryption (which probably isn't in most major browsers anyway). So you need to analyse your password protection priorities and possibly install a custom client on the user's computer, hooking the keyboard at the last possible minute and preventing it from reaching hooks lower down.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Johnathan,
dickinson.jonathan wrote: An finally, you are never safe in the first place, certain memory scrubbing programs can get a password. And keyboard hooks foil even the best memory encryption (which probably isn't in most major browsers anyway). So you need to analyse your password protection priorities and possibly install a custom client on the user's computer, hooking the keyboard at the last possible minute and preventing it from reaching hooks lower down.
With regard to the general problem cited above, I've had the opportunity to work a few enterprise contracts. In general, Remote Access is not allowed. When it is, a Terminal Server is used with RDP. So things such as hooking and memory sanitization are not an issue.
Jeff
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
"* If your data store is encrypted (using SQL Server 2005, for example), * and your internal communication network is highly secure (uses only IP-SEC or a VPN tunnel for communication between servers), * and you only use secure communication between the application client and the application server, * and you trust all employees with database access to never make mistakes (such as printing the password information or storing to file), * and you are sure nobody else has physical access to any of the servers used. Then, yes, storing passwords ‘in the clear’ is fine."
This will always evaluate to FALSE. The reason is that the last two items can NEVER be guaranteed and must therefore individiually evaluate to FALSE, so the whole becomes FALSE.
Storing clear text passwords is simply not a good idea... Ever!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Actually, it is a good idea - for hackers.
So your premise is false.;P
The madman is not the man who has lost his reason; the madman is the man who has lost everything except his reason.
--G.K. Chesterton
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Redgum wrote: Actually, it is a good idea - for hackers
Only the lazy kind that don't care about challenges.
Upcoming events: * Glasgow: Mock Objects, SQL Server CLR Integration, Reporting Services, db4o, Dependency Injection with Spring ... * Reading: Developer Day 5
Ready to Give up - Your help will be much appreciated.My website
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I have been using hashing from web page logins with an enhancement for some time. To avoid replay attacks, I introduced a login "token" or random string to append to the hashed password and then hash all of that. It works well from web pages where I have a javascript implementation of MD5.
1) Browser requests login page 2) Server renders page with embedded login token (and token ID) 3) Browser hashes password as you explained (with username salt) 4) Browser appends login token to the password hash and hashes all of that for transmssion 5) Server fetches the hashed salted password from DB by username 6) Server fetches the token by token ID (and immediately deletes the token - expires tokens older than 5 minutes also) 7) Server appends login token to password from DB and hashes all of that 8) If it matches what we get from step 4, a good login
Step 5 is still vulnerable to sniffing but this will be inside the company network where network security can be enhanced (IPSEC, wireless hardware encryption, etc). Only vulnerable if web server is on a different machine to the DB.
I use a javascript implementation of MD5 in an include file (it contains the definition of the "MD5" function used below) On the page the following Javascript code runs on form submit:
<SCRIPT LANGUAGE=JavaScript> function LogonNow() {
document.forms[0].txtHashPassword.value=MD5(document.forms[0].txtPassword.value + '-' + document.forms[0].txtUserName.value.toUpperCase()); document.forms[0].txtHashPassword.value=MD5('ba4b8bd6d49c974d87abf55e0b443cf5' + document.forms[0].txtHashPassword.value); return true; }
</SCRIPT>
"ba4b8bd6d49c974d87abf55e0b443cf5" is the token for this login attempt and was rendered inline in the javascript when the page was created.
It is critically important that the textbox txtPassword has an ID and NO NAME (so that the data is not posted to the server on postback) and the hidden inputbox called txtHashPassword has an ID and a name (so that the data is posted to the server).
The form contains a hidden input for the token ID so that the server can fetch the token from the database in step 6.
This is 100 times easier in a windows forms application where you can use the full suite of .NET hashing algorithms.
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
It's more secure if each account has its own salt, andif the salt persists each time the user changes their account you can ensure they cannot use the same password within n number of times by storing the previous hash.
The problem I'm still trying to get to grips with is how to successfully transmit a password change between two points securely.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
You can use a key exchange if you implicitly trust that you know the server's public key.
Carol = Client and Bob = Server Bob announces his public key sometime before so that Carol already knows it. B.Key Carol takes her public key, appends a cryptographically secure random number and encrypts it using Bob's public key. B.Key(C.Key+Rand1) Bob decrypts the message with his private key, recovering Carol's public key and the random number. B.Pri(B.Key(C.Key+Rand1) = C.Key+Rand1 Bob issues a cryptographically secure random number encrypts it with his private key and encrypts that with Carol's public. C.Key(B.Pri(Rand2)) Carol uses her private key to decrypt the session key, and then decrypts the next envelope using Bob's public key. B.Key(C.Pri(C.Key(B.Pri(Rand2)))) = Rand2 The session key is created xoring Rand1 with Rand2 Rand1 xor Rand2 = S.Key
Bob and Carol now use a symetrical key encryption to encrypt their packets and use the agreed upon session key... You can encrypt your password change message over this secured channel. If you assume that Eve is evesdropping, she can't use a man in the middle attack against this unless she has already compromised Bob's private key OR she has presented her own public key as being Bob's. The random number serves both as a way to increase the entropy of the message helping to prevent any "playback attack", and to check that there is no man in the middle attack going on. If Eve were to listen in, there isn't any way she could recover the random number from either Bob or Carol, and this serves as a way to validate the source. Since both parties created half of the session key, and neither party has broadcasted the key without a layer of encryption around it, both parties are well protected. This doesn't PROVE that Carol is talking to Bob, but it proves that Carol is talking to someone who knows the private key associated with the public key that is claimed to be Bob's. Since Carol decrypts the final piece using both her private key and Bob's public key, you can be pretty certain that no one is listening. Something like Kerbos uses certificates to prove that you actually know Bob's public key, but even that has a layer of trust that you must accept... that the root certifcate authority is who they claim to be.
(I'm doing this from memory, so don't use this method without double checking it, but by my spot check it works)
modified on Friday, February 01, 2008 5:00:48 AM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
What's the point of salting when the password store is compromised? I mean if you have to store the salt, the hacker would also have access to it right? He can use it just as well to decode it by brute force. Any thoughts on this?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Salting is specifically useful for when the password store is compromised. The author explained this very well. Reread the section entitled "Salting – everything tastes better with a little salt". With salting, every password will have to be deciphered with brute force. Without salting, duplicate passwords from different users will have the same encoding, so if one is broken, the other is also.
My thanks to Tamir, this is one of the best written articles I have ever seen about saving passwords, and one of the best written articles in Code Project.
Brian Leach
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
I agree.
For one, password hashes are often transmitted over a network. If an low-privilege user-attacker sniffed a hash from a high-privilege user (no password server compromise needed), they could keep hitting the log-in system with passwords from their own account and look for a match. If they get one on a saltless system, they have access to the high-privilege account.
There still are two good reasons why a compromised password server can still benefit from password salt:
1. Your password system will often be de-coupled from the main data access system. A vulnerability in a log-in page might give SELECT access to user accounts but not other sensitive data.
2. Often an attacker will not just want to steal data, but to insert malicous data as well. Doing this under a trusted account is much more useful to the attack, as most sensitive systems will have some kind of audit trail.
-IEB
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Salting with a user-specific salt (e.g. SHA2(SHA2(username) + password)) ensures that each password must be brute-forced separately. Without a salt, all passwords can be broken at once. Salting also protects against attacks with precomputed rainbow tables[^].
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
> Is storing password ‘in the clear’ an acceptable solution? Never... In addition, if subpoenaed, you must produce the password.
A fellow named Whitfield Diffie got to thinking about this issue in the 1970s. You probably know the rest of the story.
> If a user needs to retrieve a password, the stored password > is de-scrambled and provided to the user (usually via email). The password should be reset, and email to the user. At next logon, the user should be required to change the password.
Nice article otherwies.
Jeff
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
Jeffrey Walton wrote: The password should be reset, and email to the user.
Emailed 'in the clear', you mean? Anyone capturing network traffic that captures a password reset mail gets email address, password, and usually username. Emailing reset passwords is not even slightly secure - however there aren't really any good alternatives in the public internet environment.
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
Hi Craster,
Craster wrote: Emailed 'in the clear', you mean?
Sadly, yes.
Emailing a lost password at least solves the problem of the subpeona. However, it opens up a security hole for 1 account. This (in my humble opinion) is beter than clear storing of all accounts for emailing for retriveal.
Jeff
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Simple solution, the user is given a grace period in which to use their new password (something really short like 5 minutes), after which they have to request another new one. You simply don't have to give them their current password. Not ever.
Storing passwords with reversible encryption is simply not an option, unless you rely on some framework that only accepts them plaintext (e.g. connection strings).
|
| Sign In·View Thread·PermaLink | 4.00/5 |
|
|
|
 |
 | Ups!  Horia Tudosie | 7:23 29 Sep '06 |
|
 |
"if the cryptographic hash of two files is identical, then the two files are identical as well" (from your text.)
Is this assertion correct?! Or should be read the other way:
"if two files are identical, then the cryptographic hash of two files are identical as well"!
There is always the possibility that two different files to produce identical signatures, even if the probablility is very small...
Horia Tudosie
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
Yeah, and there's always the remote possibility the hash will turn into a cheeseburger and foul up the hard drive...
The madman is not the man who has lost his reason; the madman is the man who has lost everything except his reason.
--G.K. Chesterton
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
 | Re: Ups!  Dag Oystein Johansen | 9:54 16 Aug '07 |
|
 |
As a matter of curiosity, there are actually far more inputs, on average, even of such a small size as 60 bytes, that produce the same 128-bit hash code than there are atoms in the observable universe.
This may sound incredible, but its easy to prove (assuming you accept that the number of atoms in the observable universe is on the order of 10^90, or one billion billion billion billion billion billion billion billion billion billion).
Number of different 60-byte inputs = 2^(60*8) = 2^480 Number of different 128-bit hash codes <= 2^128 Average number of inputs per hash code >= 2^(480-128) = 2^352
Converting this to base 10 is a matter of multiplying the exponent with the base-ten logarithm of 2, which yields
2^352 ~= 10^105
so about one million billion times more than the number of atoms in the observable universe.
Not that this has great practical significance
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
Is there a way to reconstruct the original data from the hash?
Unfortunately, I need a way to compress 128 bytes of data that user has to enter (in order to unlock multitude libraries to be used with my program) into something more managable and MD5 allows me to squeeze it to 16 bytes. But how do I go back? I understand that hash functions in theory are one way, but can you suggest another way of seriously squeezing 128 bytes into a small blueprint that is reversable. Thanks.
|
| Sign In·View Thread·PermaLink | 3.00/5 |
|
|
|
 |
|
 |
You seem to be looking for a compression scheme. By definition, compression is reversible but one-way hashes are not. The only way to reverse a one-way hash is to pre-compute a conversion table. In your case, all the possible input variations (2^1024 combinations) and the resulting hash values. Do you require your 128->16 conversion to be unique? A classic hash function can be used to map 128 byte data to a smaller space, but can you guarantee that you will only have 2^128 different values? If you can guarantee no more than 2^128 variations of the input, you can use a mapping hash. A simple mapping hash would be to use just the lower 128 bits of the input, but the correct hash to use would depend on your input.
|
| Sign In·View Thread·PermaLink | 3.33/5 |
|
|
|
 |
|
 |
The problem is that I cannot find a compression scheme that will squeeze 128 bytes into something lower than 20 bytes (a manageable number for a user to enter).
And, it seems, from what you said about pre-computed conversion table that it would be higly inefficient to use such a scheme as it will take a lot of space in memory.
I cannot guarantee that I will only have 2^128 different inputs - I CAN guarantee that I will have 2^1024 different inputs. But you are telling me that a classic hash function will cover only 2^128 unique inputs? Is that correct? What should I do then for my situation where I have significantly more permutations?
Thank you
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
On the one hand you said I need a way to compress 128 bytes of data that user has to enter , then later you said
something lower than 20 bytes (a manageable number for a user to enter).
I am assuming that you want the user to enter the longer information once, and then you will return a 20 byte or less key for them to get it in the future. Why don't you just use a lookup table? Your requirements dictate that the 128 bytes be decipherable, so one way encryption isn't going to work.
Brian Leach
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|