ron rothman.ron rothman
selectively conformist

Password Security: It’s Not That Hard (But You Still Can’t Get It Right)

I want to talk to you about password security.

Actually, that’s a lie. I don’t want to talk about it at all. But I have to–someone has to. I’ve had two recent problems with password security, both of them shamefully avoidable.

Warning: I’ll be discussing how to securely store passwords on a website (or in a database). This is primarily for website architects and developers, not for the general public.

What Not to Do

It all started 3 months ago. I’m a member of a certain web-based community (which I shall call Site X–for my protection and theirs). Membership in Site X is not free; you pay to join, and you log in with your user name and password to participate.

As a website operator, you are a password steward: it is your obligation to protect your users’ passwords.

Someone left me a message on Site X, and the website’s software sent me an automated email notifying me that I had received this new message. To my horror, the email included both my user name and password.

If you’re gasping in horror at that last sentence, then you needn’t read the rest of this; you already know what I’m going to say. But if you’re wondering, “What’s the big deal? So they emailed you your password,” then you should continue reading.

First, to wrap up my anecdote: I had an email exchange with site X’s webmaster. The emails started out rather cordially. Okay, that’s another lie. My email to them went something like this:

hello,

PLEASE tell me you didn’t really just send me an email
with my user name and password in plain text. you should fix this *major* security flaw very soon.
– ron

Their reply (which I’m including here because I think it underscores some website owners’ basic misunderstandings about password security):

why would this be a major security flaw? has
someone broken into your yahoo account before? only
you have your password for yahoo…correct?

I had to take a deep breath before replying to that one. I waited 7 days, because I know the emotional damage I’m capable of inflicting with my saber-sharp wit when I write rashly–particularly on the ignorant (bless their little souls).

I canceled my membership at Site X–despite wanting to use their service.

One sunny morning, three months later, I tried to log in to my telephone account on sprint.com, but I’d forgotten my password. (Yes, the gloves are off now; I feel no need to hide Sprint’s identity.) No problem, I just asked for a password reset. Simple enough. I expected to get an email with my new, temporary password. Here’s the email I got instead:

Per your recent online request your Sprint Local online password has been reset. Your login information is below. Please make note of this information for future reference.

User ID: rothmanron
Password: [my original password was here]

Ack! It happened again! And this time, not with some dinky mom-and-pop site, but with Sprint.com! Shouldn’t they know better?

After a surprisingly uncaustic email to Sprint (no response yet, btw), I asked around, and found that these unsafe password practices are a lot more common that I expected.

Let me explain why it’s evil to email users their passwords. The real reason may not be obvious to you. But first, for completeness, let’s talk about the more obvious reason.

The Obvious Reason That Emailing Passwords Bad

A. Security is based on a weakest-link principle.
B. Email is not secure.

Therefore,

Emailing a password compromises the security of the account whose password is emailed.

I imagine that Site X’s webmaster would respond, “But email accounts are secure–they’re password protected.” (“has someone broken into your yahoo account before? only you have your password for yahoo…correct?”)

1. That’s wrong.

Email is inherently insecure. Before it reaches its destination, it travels through internet relays which you have no control over and which have complete access to its contents.

2. It’s beside the point.

As a website operator, you are a password steward: it is your obligation to protect your users’ passwords. You can be as careful as is humanly possible within your own systems, but once you send a password to an external system, you have no control over its protection, and no guarantees about its security. It doesn’t matter whether Yahoo email is secure; the point is that you don’t control Yahoo’s email security. You only control your own systems’ security. Remember the weakest link principle.

As an aside, Yahoo’s email is not all that secure. Any hacker will tell you that the most common methods of acquiring passwords are low-tech: social engineering, phishing, etc. In any case, your users may not even be using an email provider as secure as Yahoo. They may be using mom-and-pop-email.com.

Okay, remember all that stuff about email being insecure? Well, forget it. It doesn’t even matter here.

The point is: if you send out passwords in email, you’ll have to worry about these external weaknesses. And since you can’t do anything whatsoever to mitigate them (um, that’s what I mean by external, after all), the only thing you can do is avoid emailing people their passwords.

If you still think that email is secure, then you are better at denial than I thought. But I’m not through with you. In the end, this whole email-is-insecure discussion isn’t even relevant. Read on to see why.

The REAL Reason That Emailing Passwords Bad

Okay, remember all that stuff about email being insecure? Well, forget it. It doesn’t even matter here.

Was it all a vicious lie? No, no, it’s all true. But there’s another problem with Site X’s password practices which dwarfs the email issue: they stored my unencrypted password in their database.(!)

Once again, if reading that made you feel slightly nauseated, then you already know what I’m about to explain. But if you don’t see the problem yet, please continue reading.

The Problem: Storing Unencrypted Passwords

No website should store a user’s password unencrypted. Ever.

Site X’s webmaster wants to ask me, “Why not? After all, I have to authenticate users when they log in, so I need their passwords, right?. And besides, we run a very tight ship here–no hacker can gain access to our data.”

Wrong, and wrong. (Site X dude is not having a good day.)

1. You don’t need to store users’ passwords in plaintext in order to authenticate them.

2. Your site is not secure. I repeat: your site is not secure.

Let’s look at each of those a little closer.

You Don’t Need the Plaintext

There are well-known, time-tested authentication methods going back 40+ years which don’t require storage of plaintext passwords. They exist for a reason: if you store plaintext passwords, you create a security hazard for your users. Remember that users typically use the same password across multiple sites (they have to; it’s too hard to manage dozens of passwords), so by storing their unencrypted password, you potentially expose not only their account on your system, but on other systems as well.

You Are Not Secure

“But as long as no one unauthorized can access my users’ plaintext passwords in my database, my users have nothing to worry about. Right?”

Well, strictly speaking, that’s true. But you have no way to guarantee to your users that no one unauthorized will gain access.

What’s that? You think your data is secure? I see.

VISA thought that their data was secure. But see Visa Deals With Possible Data Breach.

Bank of America thought that their data was secure. But see Bank of America Cancels Numerous Debit Cards.

OfficeMax thought that their data was secure. But see Prosecutor: Debit Card Crime Ring Busted.

FedEx thought that their data was secure. But see FedEx Kinko’s Payment Card Cracked.

But see FBI Makes Connections in Data Breach Case.

But see Banks Scramble on Debit Card Theft.

But see, but see, but see. Do you see yet?

Do you really expect your customers to think that you’re more secure than VISA International? Than Bank of America? (Do you really think that you are?)

Okay, that’s the bad news. But there’s good news, too: there is a way to authenticate your users without compromising their security. It’s not even that hard. I’ll explain it below.

A Very Bad Solution: Storing Plaintext

By now you already agree that storing unencrypted passwords is a bad idea. But I’ll try to describe the way Site X authenticates its users, just to illustrate the differences between the wrong way and the right way of doing things.

When you register as a member of Site X, they create a record for you in their database. They store, among other things, your user name, your password (in plaintext), your billing address and phone number, email address and probably your credit card information.

// user enters name, address, email, CC info, user name, password

INSERT INTO Users
VALUES ($userName, $name, $address, $email, $ccNumber, $password);

When you log in, you input your user name and your password. They look up your record using your user name, retrieve your password, and check whether enteredPassword == dbPassword. If so, they let you in (and send you an authenticated session cookie), else they deny you access.

// user enters user name and a password ($userName, $password)

// look up user's record
$dbPassword = 
   SELECT password
   FROM Users
   WHERE userName = '$userName';

if ($enteredPassword eq $dbPassword) {
   // authenticated.  grant access.
}
else {
   // wrong password.  deny access.
}

If you forget your password, they ask you to enter your user name. They look up the email address and password associated with that user name in their database, and send an email to that address with the password in plaintext, so that the recipient of that email (presumably you) can log in.

If someone replies to one of your postings on Site X, Site X sends you an email telling you that you have a reply waiting for you. This email includes your user name and password.

Now, let’s look at how to fix this mess.

A Good Solution: Storing Passwords Securely

Let’s build Site Y, a competitor to Site X, whose competitive advantage (other than hiring better webmasters) will be greater security than Site X. (That is to say, any security.)

When you register with Site Y, we will not store your unencrypted password in our database. What we’ll do instead is compute a salted hash of your password, and store that in our database. We’ll also have to store the salt characters.

// user enters name, address, email, CC info, user name, password

// choose random salt characters
static const $saltCharset = qw(a b c ... z A B C ... Z 0 1 ... 9 + - * / ...);
static const $saltCharsetSize = scalar $saltCharset;

$saltChar1 = saltCharset[rand($saltCharsetSize - 1)];
$saltChar2 = saltCharset[rand($saltCharsetSize - 1)];
$salt = $saltChar1 . $saltChar2;
$saltedPassword = $salt . $password;

// compute the hash value (use a 3rd party library for this)
$hashVal = compute_hash($saltedPassword);

INSERT INTO Users
VALUES ($userName, $name, $address, $email, $ccNumber, $hashVal, $salt);

If you don’t know what a hash is, you can think of it as a function which takes as input a string (e.g., a password), and outputs a large number, typically ranging anywhere from 16 to 64 bytes, which is derived from the input string.

Why are we using a hash? To solve the primary problem I outlined above–storing unencrypted passwords. A hash is effectively an encrypted version of a user’s password. It’s considered encrypted, because there’s no [easy] way to figure out what the password is, given only the hash value.

What does “salted” mean? Salting means that we’ll add a couple of randomly-generated characters to the password before we compute the hash value. Then, we store the hash value and the salt (the random characters) in our database.

Why do we salt the input to the hash function? As an added precaution. By salting, we ensure that users who happen to choose the same password will have different hash values. So if Hacker Bob were to gain access to our database, and if he were able to reverse-compute one user’s password from its hash value (which would be a mighty hard task), he wouldn’t automatically know any other user’s password–even those users who happened to choose the same exact password as Hacker Bob’s victim.

Computing a salted hash is not complicated. Every major web publishing platform (java, php, perl, …) has an MD5 or SHA library which contains functions to do just that, so we can really think of it as a black box. All we’ll have to do is choose a few random characters as our “salt”

So much for registration. Now, when you log in to Site Y, you input your user name and your password. We look up your record using your user name and retrieve your hash value and the salt we used when you registered. We combine this salt with the password you just entered, and compute a hash value. If this computed value matches the hash value we stored in the database when you registered, we let you in; else, we don’t.

// user enters user name and a password ($userName, $password)

// look up user's record
($dbHashVal, $salt) = 
   SELECT hashVal, salt
   FROM Users
   WHERE userName = '$userName';

// compute a hashVal based on the input password
$loginAttemptHashVal = compute_hash($salt . $password);

// compare computed hashVal to hashVal in DB
if ($loginAttemptHashVal eq $dbHashVal) {
   // authenticated.  grant access.
}
else {
   // wrong password.  deny access.
}

If someone leaves you a message on Site Y, we will email you to let you know that a message is waiting; but we will not include your password. (How could we? We don’t store it! :) ) So no worries there.

And finally, if you forget your password, we will send you an email with a link to a one-time password reset page. We’ll also ask for some other information which you entered at registration (maybe day of birth, or zip code), and then we’ll let you reset your password.

Since Site Y doesn’t store unencrypted passwords in its database, we’ve removed the major security flaw inherent in Site X. Now, as long as we can avoid creating other weak links, our site will be more secure than Site X, and we can begin the media campaign to frighten their customers into switching sites.

Concluding Thoughts

Don’t be a Site X.

6 Responses to “Password Security: It’s Not That Hard (But You Still Can’t Get It Right)” [Leave yours »]

  1. Amen. Excellent post. Hopefully the slacker doofuses will read it. (Sending a link to site X’s webmaster? hehe) Scary thing that your payment information might be stored alongside your other information there isn’t it?

    1
  2. Daniel said:

    What do you do about the password being sent in plain text? Suppose this is a mom and pop website and pop won’t buy an SSL certificate + SSL hosting.

    2
  3. Ron [author of post] said:

    What do you do about the password being sent in plain text?

    what can i tell you? either your http connection is secure, or it isn’t. and either pop pays for security (and possibly more customers), or he doesn’t.

    but if sending an unencrypted password is your weakest link, then you’re already a lot more secure than many [large-scale] websites.

    3
  4. Cesar said:

    Congratulations Ron, it’s a very good article.

    I’m developing a Java Enterprise Application for a school management (like a small ERP with various modules communicating) and, besides it’s an Intranet web application, without external access to it, I encrypt all users passwords with the hash() java method, before store it to the database. Do you think that’s a good way of encrypt passwords for such type of application?

    Unfortunetely the school hasn’t much financial resources, so I have the application server and database server on the same computer. Is there any major issue with that?

    Thank you.

    4
  5. Pedro said:

    Hello,

    I am, sorry, was a Yahoo user. That is until they or some hacker fudged my password. Over the past few weeks (January 2007), I have been increasingly irritated by a rising barrage of password demands from Yahoo. In the past ten days or so, I have even been asked for my password immediately after successfully logging on … and then again sporadically, maybe a few times an hour and on a couple of occasions in quick succession.

    Then it happened. I was denied access. I was stunned. Yahoo was telling me that either my Yahoo ID or password was incorrect. I was only just reading a message and wanted to check my mail again, got asked for my password *SIGH* … and now whammo I can’t access my account!!!

    I can’t decide if my account was hacked or if there was a glitch in Yahoo’s password system. Initially I suspected I have been hacked … but now I am beginning to suspect that in Yahoo’s zeal to ramp up random password validation requests, that they have created such a storm of such validation requests that somewhere along the line their password cache memory got garbled. If I am right, there should be a rising tide of Yahoo users who are or will soon also be in my situation.

    Now I am in the loop trying to access my account again. Reading some of the posts out there, I am not optimistic. I have to wait 48 hours, they say. Also, now they are demanding ALL the information I provided when I created my account … and I was one of their early adopters. What on earth did I tell them then? I am sure I was a little evasive … not wanting to give any more information away that was strictly necessary. I do recall being asked to give a secret answer to a challenge question and am sure I found a good one that only I would know. I never, however, counted on the answer AND the question being secret. Apparently now, they want me to tell them BOTH the question and the answer. This is not reasonable. Who would expect that you have to remember some obscure question you set maybe a decade earlier. Certainly it would be a good one that only you knew the answer to.

    So far all I have are auto-responses … I am praying that some sentient being will read my please for help and will have the common sense to realize that I have provided more than ample information to convince them that I am the rightful owner. But I also know at one person who lost their Yahoo account because a former room mate installed a key-logger on their computer and then took-over their account.

    Even though this ex-room mate went on to post hateful content on their profile (about the supposed account owner) Yahoo was not convinced to return control to the original owner. I should add, however, that in this case this individual made-up entirely fictional data when they created their account in the first place … so there was no hope that they could validate the data that Yahoo had on file.

    I am now waiting to see if Y! resets my account and sends me a new password and, if so, if it is in plain text. hmmm, silly question. Well even if I get my account back, will I still have all my email and Y! Messenger contacts?!? Or will my on-line world come crashing down.

    Does anyone want to register YahooUsers.org? (Yahoo already has YahooUsers.com)

    Pedro

    5
  6. alex farguson said:

    Thanks for this. Just subscribed.

    6

Leave a Reply

Comment formatting tips are available.


Your comment will appear on the site once it's approved. (Please read the COMMENT POLICY before posting.)