How to create human readable randomized passwords?


This issue came up in my day job where the client didn’t like our password reset passwords. They said it was too random and people can’t remember it long enough between reading it in there email and entering it into our app/website.

This might be a matter of taste since sending out a password change link is another common solution, but we don’t have that flow in our existing implementation. Generating a less random temporary password is the quicker solution.

So the method we chose was to pick a random 4 letter word from a dictionary file and append a number to the end of it.

We found a hacker’s dictionary online and used that as our base. To restrict it to just 4 letter words I ran:

cat en-US.dict | ruby -ne 't = $_.strip; puts t if t =~ /^\w{4}$/' > passwd.dict

What is this doing?

(skip this if your already familiar with Bash and Ruby)

  1. cat en-US.dict print the contents of en-US.dict to stdout.
  2. | ruby -ne '...' direct to output of cat into ruby. a. -n tells ruby to iterate through all the lines and set the global $_ variable to the current line. b. e tells ruby to execute the next argument as a ruby script
  3. t = $_.strip remove any leading or trailing whitespace and set it to variable t
  4. ; ruby line separator so we can do this as a one liner
  5. puts t print the value of t
  6. if t =~ only execute the previous line puts t if t matches the following regular expression.
  7. /^\w{4}$/ this is a regular expression that matches any t which contains exactly 4 characters between [a-z] and [A-Z]
  8. > passwd.dict redirect anything which was puts by step 5 into a file named passwd.dict

Random Word Selection

The next part was the random selection. It isn’t that fancy, but I figured I shouldn’t leave it out.

To restrict it to just 4 letter words I ran:

def random_passwd
  words ='passwd.dict').split  # read the contents of passwd.dict and
                                          # convert it into an array. 1 element per word.

  word = words[(rand * words.size).to_i]  # pick a random element.
  num = (rand * 8).to_i + 2               # pick number between 2 and 8.
  word + num.to_s                         # concatenate the two strings together.

That’s about it.

Misc Points of Interest

  1. The file read maybe cached, but all the 4 letter words in the English language was only ~2500, and the cost of reading the file was less than a a millisecond.
  2. The passwd.dict file has an extra line feed at the end so we took that out manually. We were editing the file anyway to remove the potentially offensive 4 letter words.
  3. The regex can be changed to taste, but we figured 4 letter words are easily remembered.
  4. In our actual implementation, we further restrict the word list so the words can’t end in “l” or “o” so the user wouldn’t confuse them with numbers. This is the same reason we don’t pick a random number starting with 0 or 1.
  5. You’ll most certainly want to force the user to change the password after entering this one. The whole point was to have a weak password so the user could remember and type it.

Hope this can be helpful to some one out there.