Redemption Steps

Theres a whole lot you can do here, so hold on to your hat! Here goes.

Multifactor Authentication

Some security people may like to say that password technologies are dead, and theyll argue that you shouldnt use them at all. The opposite is really true. There are three broad classes of authentication technologies:

  • Things that you know This refers to PINs, passphrases, and whatever other term you can come up with that is essentially a synonym for password.

  • Things that you have This refers to smart cards, cryptographic tokens, your credit card, SecurID cards, and so on.

  • Things that you are This generally refers to biometrics.

All three classes have their pros and cons. For example, physical authentication tokens can be lost or stolen. Biometric data can be captured and mimicked (either by, say, a fake finger, or by injecting bits that represent the finger directly into the system). And, if your biometric data is stolen, its not like you can change it!

All three of these techniques can be strengthened by combining them. If an attacker steals the physical token, the attacker still needs to get the password. And, even if a person leaves a password under the keyboard, it still requires the attacker get the physical device.

One important point here is that the system only gets stronger if you require multiple factors for authentication. If it becomes either-or, then an attacker only needs to go after the weakest link.

Storing and Checking Passwords

When storing passwords, you want to make the password storage function one-way so that it cant be decrypted. Instead, attackers will need to take guesses and try to find a match. You should also do as much as possible to make that match-finding function difficult.

A standard and quite sound way to do this is to use PBKDF2 (password-based key derivation function, Version 2.0), which is specified in the Public Key Cryptography Standard #5 (PKCS #5). While the function was originally intended to create a cryptographic key from a password, it is good for what we need it for, as it is a standard, public function that meets all of the requirements weve discussed. The output is one-way, but deterministic. You can specify how much output you wantyou should be looking for 128-bit validators (16 bytes), or larger.

The function also has some functionality that helps protect against brute force attacks. First, you get to specify a salt, which is a unique random value meant to help prevent precomputation attacks. This salt is needed to validate the password, so youll need to store it along with the output of PBKDF2. An eight-byte salt is plenty if you choose it at random (see Sin 18).

Second, you can make the computation take a relatively long time to compute. The idea is that if a legitimate user is trying a single password, then the user probably wont notice a one-second wait when typing in a password. But if an attacker has to wait a second for each guess, an offline dictionary attack could end up a heck of a lot harder. This functionality is provided by supplying an iteration count , which dictates how many times to run the core function. The question becomes, How many iterations? The answer to that has to be based on how long youre willing to wait on the cheapest hardware you expect to run on. If youre running on low-end embedded hardware, 5,000 iterations is about the right number ( assuming the underlying crypto is written in C or machine language; you do want it to be as fast as possible, so you can do more iterations). Ten thousand is a conservative general-purpose number for anything other than low-end embedded hardware and 15-year-old machines. Modern desktop PCs (anything Pentium 4 class or similar) can do fine with a setting of 50,000 to 100,000 iterations. The problem is that the lower the number, the easier the attackers job is. The attacker isnt limited to embedded hardware for launching an attack. Of course, too many iterations can slow your application down if it performs many authentications. You should start high and benchmark your application; dont assume the system will be slow simply because you have a high iteration count.

For what its worth, the Data Protection API in Windows (see Sin 12) uses PBKDF2 with 4,000 iterations to increase the work factor of an adversary trying to compromise the password. Thats definitely on the low-end if you only expect to be supporting reasonably modern OSes running on reasonably modern hardware (say, the last five years ).

Some libraries have the PBKDF2 function (most actually have an older version that isnt quite as well designed), but it is easily built on top of any Hash-based Message Authentication Code (HMAC) implementation. For instance, heres an implementation in Python where you supply the salt and the iteration count, and this produces an output that can be used as a password validator:

 import hmac, sha, struct   def PBKDF2(password, salt, ic=10000, outlen=16, digest=sha):  m = hmac.HMAC(key=password,digestmod=digest)  l = outlen / digest.digestsize  if outlen % digest.digestsize:  l = l + 1  T = ""  for i in range(0,l):  h = m.copy()  h.update(salt + struct.pack("!I", i+1))  state = h.digest()  for i in range(1, ic):  h = m.copy()  h.update(state)  next = h.digest()  r = ''  for i in range(len(state)):  r += chr(ord(state[i]) ^ ord(next[i]))  state = r  T += state  return T[:outlen] 

Remember, you have to pick a salt and then store both the salt and the output of PBKDF2. A good way to choose a salt is to call os.urandom(8), which will return eight cryptographically strong random bytes from the operating system.

Lets say you want to validate a password, and youve looked up the users salt and validator. Determining whether a password is correct is then easy:

 def validate(typed_password, salt, validator):  if PBKDF2(typed_password, salt) == validator:  return True  else:  return False 

Note that this uses the default of SHA1 and an iteration count of 10,000.

The PBKDF2 function translates easily into any language. Heres an implementation in C using OpenSSL and SHA1:

 #include <openssl/evp.h> #include <openssl/hmac.h> #define HLEN (20) /*Using SHA-1 */ int pbkdf2(unsigned char *pw, unsigned int pwlen, char *salt,   unsigned long long saltlen, unsigned int ic,  unsigned char *dk, unsigned long long dklen) {  unsigned long l, r, i, j;  unsigned char txt[4], hash[HLEN*2], tmp[HLEN], *p = dk;  unsigned char *lhix, *hix, *swap;  short k;  int outlen;  if(dklen > ((((unsigned long long)1)<<32)-1)*HLEN) {  abort();  }  l = dklen/HLEN;  r = dklen%HLEN;  for(i=1;i<=l;i++) {  sprintf(txt, "%04u", (unsigned int)i);  HMAC(EVP_sha1(), pw, pwlen, txt, 4, hash, &outlen);  lhix = hash;  hix = hash + HLEN;  for(k=0;k<HLEN;k++) {  tmp[k] = hash[k];  }  for(j=1;j<ic;j++) {  HMAC(EVP_sha1(), pw, pwlen, lhix, HLEN, hix, &outlen);  for(k=0;k<HLEN;k++) {  tmp[k] ^= hix[k];  }  swap = hix;  hix = lhix;  lhix = swap;  }  for(k=0;k<HLEN;k++) {  *p++ = tmp[k];  }  }  if(r) {  sprintf(txt, "%04u", (unsigned int)i);  HMAC(EVP_sha1(), pw, pwlen, txt, 4, hash, &outlen);  lhix = hash;  hix = hash + HLEN;  for(k=0;k<HLEN;k++) {  tmp[k] = hash[k];  }  for(j=1;j<ic;j++) {  HMAC(EVP_sha1(), pw, pwlen, lhix, HLEN, hix, &outlen);  for(k=0;k<HLEN;k++) {  tmp[k] ^= hix[k];  }  swap = hix;  hix = lhix;  lhix = swap;  }  for(k=0;k<r;k++) {  *p++ = tmp[k];  }  }  return 0; } 

The following code is written in C#:

 static string GetPBKDF2(string pwd, byte[] salt, int iter) {  PasswordDeriveBytes p =  new PasswordDeriveBytes(pwd, salt, "SHA1", iter);  return p.GetBytes(20); } 

Guidelines for Choosing Protocols

If youre authenticating people in a preexisting environment where theres an existing password infrastructure built on Kerberos, then you should just use that existing infrastructure. In particular, do this when authenticating users on a Windows domain.

If it doesnt make any sense to do this, the answer is going to vary based on the application. In an ideal world, one would use a strong password protocol (that is, a zero-knowledge protocol ) such as Secure Remote Password (SRP; see http://mv.ezproxy.com), but few people currently use these protocols because there may be intellectual property concerns.

Once you step below strong protocols, there are going to be risks with any protocol you might use. We recommend picking whatever seems to make sense, even if it is type in the password, compute the hash, and then send the hash to the server (which is slightly better than sending the password to the server and letting it compute the hash itself). However, you should run any protocol you do use only after establishing an SSL/TLS connection (or something providing similar services), where the client properly authenticates the server using the guidelines we give in Sin 10.

If youre not able to use a protocol like SSL/TLS for some reason, and youre not willing to risk using a strong password protocol, then we strongly suggest you consult a cryptographer. Otherwise, youre highly likely to mess up!

Guidelines for Password Resets

Locking users out of their accounts for too many bad password attempts is a DoS waiting to happen. Average users will eventually decide they dont remember their passwords, and go through whatever processes you have in place to reset them.

Instead, limit the number of attempts to some reasonable number like 50 in an hour , and then use some basic logic to try to detect possible attacks, under the theory that a legitimate user isnt often going to try to log in more than 50 times in a day.

An alternative that has a similar effect is to slow down the authentication process once you get a few bad login attempts. For example, if you notice three bad logins in a short period of time, you might delay server-side messages so that the login protocol always takes ten seconds (back off on this when the attack appears to have stopped ).

This is only effective if you limit the number of simultaneous login attempts. Limiting the number to one at a time is a good idea, anyway, and actually most strong protocols cant be proven to meet their security goals without such a restriction.

We recommend that dealing with attackers be an operational concern. Often, youll want to blacklist offending IP addresses at a network level. Additionally, if a large number of attempts have been made on a particular account, you might want to alert the user and encourage the user to change the password on the next login.

When users do decide they need to reset their passwords, make it really tough or even impossible for a human to do this. It should only be operationally possible if users can pretty much prove their identities using a thing they have or two, such as photocopies of drivers licenses, and perhaps even social security cards.

Yet, passwords are often forgotten, so you will want to make automated resets easy. While e-mail itself suffers from a lot of problems in the network security sense, allowing people to receive temporary, new, randomly generated passwords in e-mail is a lot better than introducing a human into the loop who will be susceptible to social engineering. Certainly, its still a risk, particularly in corporate environments where e-mail often can be visible to users on a local network.

Password reset questions provide a good barrier to e-mailing a new temporary password out, because they allow some level of assurance that its actually the legitimate user asking for a reset. We also recommend that if you use this kind of approach, you also e-mail reset passwords as opposed to letting people choose their own at that time, because it makes the e-mail another hurdle an attacker has to jump. As youll recall, this would have stopped the Paris Hilton T-Mobile attack.

To improve upon the protection provided by password reset questions, consider creating a large database of questions on obscure topics that ask for things that may not be relevant to everybody. Then, allow users to pick questions that theyre likely to remember. This generally makes the attackers information gathering task more difficult.

Guidelines for Password Choice

People generally dont like to have randomly selected passwords, because theyre difficult to remember, and theyll always live at least part of their life on a piece of paper. We personally think that the piece of paper approach is fine as long as that paper lives in a wallet or purse instead of under a keyboard, but thats difficult to ensure. As a result, we dont recommend forcing people into random passwords, but its conceivable that this may be an option you give people. Really, people who are paranoid enough will pursue their own random password generators.

A better approach is to try to ensure some minimal password quality. This is a narrow rope to walk because the usable thing is to allow bad passwords. Users will get frustrated, and theyll be more likely to have to write things down, the more difficult you make it to jump the password quality bar.

Its reasonable to require a minimal password length of six to eight characters (dont have an arbitrary maximum). Some people like to enforce the presence of nonletter characters (and sometimes nonnumber characters ), which we also think is reasonable. One approach that works pretty well is to let people know whether brute-force searches would turn up their passwords by actually running brute-force searches on them. In an enterprise environment, this is usually a task for the IT staff. But, you can hook one of these things up to your software and give it a shot when the user first tries a new password, too. Theres even one library, called CrackLib, available from the open source world that does exactly this kind of testing. Its available from www.crypticide.com/users/alecm/.

Its best to check for weak passwords when the user is choosing a password. If you find a weak password after the fact, you can be less sure its not a compromised password!

An easier thing you can do is to give people ideas on how to create good passwords. For example, you might suggest using a short quote from a favorite book, movie, and so forth.

Its often considered good practice to make users change their passwords with some regularity (say, every 60 days). This is a mandatory practice in some industries, though its viewed skeptically in others. This is because, while it does take away some risk, it also adds new ones. When people are faced with a system that is difficult to use, it can make them do things they dont necessarily want to do. In particular, people often reuse old passwords, or choose easier-to-guess passwords because they have difficulty keeping up with the password changes.

In environments where frequent password changes are a good idea, you should also be remembering old passwords, to make sure that people dont rotate through a small set of passwords. Generally, you should do this by storing a validator, not the actual passwords themselves .

Other Guidelines

One incredibly important thing is to make sure that, after the password protocol runs, youre left with a secure session that provides at least ongoing message authentication, if not also encryption. The easiest way to do this is to either use a strong (zero-knowledge) password protocol that also does a key exchange, or to properly set up an SSL/TLS connection before performing the password authentication (see Sin 10).

Also, make sure that you use a password entry mechanism that doesnt echo the password when users type it. Its better to avoid echoing anything, though many dialog boxes will show asterisks, or something similar. The asterisks approach reveals password length, and it can help make timing attacks easier for someone who manages to train a camera on a screen from a remote distance. Overall, this is probably going to be the least of your password worries, however.



19 Deadly Sins of Software Security. Programming Flaws and How to Fix Them
Writing Secure Code
ISBN: 71626751
EAN: 2147483647
Year: 2003
Pages: 239

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net