DEV Community

Cover image for Human Readable Passwords
Peter van der Does
Peter van der Does

Posted on

Human Readable Passwords


For a Django application, I had to create passwords for users who sign up on a website. Generating passwords can be relatively easy.

Take a look at this function.

def generate_password(length=10):
 return "".join(
        random.choice(string.ascii_letters + string.digits) for _ in range(length)
Enter fullscreen mode Exit fullscreen mode

This is the result

>>> print(generate_raw_password())
>>> B61CbF9GdW
Enter fullscreen mode Exit fullscreen mode

This is secure but hard to remember.

To make them more user-friendly I thought I would create a function that is inspired by This website creates human-readable passwords which are secure. An example is 3~shoes~FOUND@ and this is much more human-readable than the previous method.


How did I go about creating the human-readable passwords?

Word List

I had to start with a list of words. With Wordle being so popular finding a list of 5-letter words is not that hard. There are many Worlde clones and GitHub has a whole bunch of Wordle clones with such a list. But in all honesty why not go to the source of the original Wordle and see if you can get their list.

NY Times Wordle

The NY Times bought the original Wordle and the source is no longer available. You can still get the list with a little bit of hacking.
Open this link: Search for ,Oa= and there you have the start of the list. There are about 10K words in this list. When I checked the list I did find some words that people might find slightly inappropriate, d*cks and that's not the bird, b**by.

On your computer

I found another way to generate a list of 5-letter words. Most Linux-based machines and Macs have a words list on the system, /usr/share/dict/words. To extract all 5-letter words we can write a simple one-liner in Perl.

perl -nle 'print if /^[a-z]{5}$/' /usr/share/dict/words > words5.txt
Enter fullscreen mode Exit fullscreen mode

And there we have a list. Unfortunately, the full list contains words from 1934 and I found some words in the list that we no longer use.

The Stanford GraphBase

Donald Knuth created a list of five-letter words. This list is used to run various combinatoric experiments, graph algorithms, and other algorithms to explore the relationships among these words. The list is in the public domain and has 5757 words.

This is the list I'm using right now to create the human-readable password.

Using the list

Instead of putting the word list in a Python list, I decided to put the list in a DB instead. This makes it easier to add more words later if that is preferred.

The model is super easy.

# code/

class Word(models.Model):
    word = models.CharField(max_length=10, unique=True)

    def __str__(self):
        return self.word

Enter fullscreen mode Exit fullscreen mode

I will leave it up to you on how to fill the model.


The password must meet the following requirements

  • Prepend and append the password with at least 1 digit and at most 2 digits.
  • Separate each word by a special character.
  • Each word will go through a transformation of being either all lower case, all upper case, or capitalized.
  • Do not repeat the words within the password.

I also want to have the flexibility of using as many words as I want, by default I'll use 3 and will not allow less than two words.


# code/

import random

from models import Word

def create_password(words=3):
    Generate a password

    words: int

    if words < 2:
        raise ValueError
    numbers = random.sample(range(1, 99), 2)
    special_character = "!@$%^&*-_+=:|~?/.;"
    separator = random.sample(special_character, words - 1)
    selected_words = get_random_words(words)
    password = str(numbers[0])
    for step in range(words - 1):
        password = password + transform_word(selected_words[step]) + separator[step]
    password = password + transform_word(selected_words[words - 1]) + str(numbers[1])
    return password

def get_random_words(amount):
    Get a list of unique words

    amount: int

    list of str

    total_word_count = Word.objects.all().count()
    ids = random.sample(range(1, total_word_count), amount)
    selected_words = list(
        Word.objects.filter(id__in=ids).values_list("word", flat=True)
    while len(selected_words) < amount:
        id = random.randint(1, total_word_count)
        if id in ids:
        selected_word = Word.objects.get(id=id)
        if selected_word:
    return selected_words

def transform_word(word):
    Transform a string

    word: str

    transform = random.randint(1, 3)
    if transform == 1:
        transformed_word = word.capitalize()
    elif transform == 2:
        transformed_word = word.lower()
    elif transform == 3:
        transformed_word = word.upper()
    return transformed_word

Enter fullscreen mode Exit fullscreen mode

Some examples of passwords generated by this function:

Enter fullscreen mode Exit fullscreen mode


The code allows you to create human-readable passwords. It is easy to expand the words used by adding them to the database.


  • I use my selection of special characters instead of string.punctuation. I did not want certain characters from that list like the quotation marks.


Found a typo?

If you've found a typo, a sentence that could be improved or anything else that should be updated on this blog post, you can access it through a git repository and make a pull request. Instead of posting a comment, please go directly to GitHub and open a new pull request with your changes.

Photo by Colin Lloyd -

Top comments (0)