Введение
Для приложения Django мне нужно было создать пароли для пользователей, регистрирующихся на сайте. Генерировать пароли может быть относительно просто.
Взгляните на эту функцию.
def generate_password(length=10):
return "".join(
random.choice(string.ascii_letters + string.digits) for _ in range(length)
)
Вот результат
>>> print(generate_raw_password())
>>> B61CbF9GdW
Это безопасно, но трудно запомнить.
Чтобы сделать их более удобными для пользователя, я решил создать функцию, вдохновленную сайтом https://xkpasswd.net/s/. Этот веб-сайт создает человекочитаемые пароли, которые являются безопасными. Например, 3~shoes~FOUND@
, и это гораздо более человекочитаемый пароль, чем предыдущий метод.
Подробности
Как я создавал человекочитаемые пароли?
Список слов
Я должен был начать со списка слов. С учетом популярности Wordle найти список слов из 5 букв не так уж сложно. Существует множество клонов Worlde, а на GitHub есть целая куча клонов Wordle с подобным списком. Но если честно, почему бы не обратиться к источнику оригинального Wordle и не посмотреть, сможете ли вы получить их список.
NY Times Wordle
NY Times купила оригинальный Wordle, и источник больше не доступен. Вы все еще можете получить список с помощью небольшого взлома.
Откройте эту ссылку: https://www.nytimes.com/games/wordle/main.bd4cb59c.js. Найдите ,Oa=
и вот вам начало списка. В этом списке около 10 тысяч слов. Когда я проверял список, я нашел несколько слов, которые люди могут счесть слегка неуместными, d*cks и это не птица, b**by.
На вашем компьютере
Я нашел другой способ создания списка 5-буквенных слов. Большинство компьютеров на базе Linux и Mac имеют список слов в системе, /usr/share/dict/words
. Чтобы извлечь все 5-буквенные слова, мы можем написать простую однострочную строку на языке Perl.
perl -nle 'print if /^[a-z]{5}$/' /usr/share/dict/words > words5.txt
И вот у нас есть список. К сожалению, полный список содержит слова 1934 года, и я нашел в нем несколько слов, которые мы больше не используем.
Стэнфордская база данных GraphBase
Дональд Кнут создал список пятибуквенных слов. Этот список используется для проведения различных комбинаторных экспериментов, графовых алгоритмов и других алгоритмов для изучения взаимосвязей между этими словами. Список находится в открытом доступе и содержит 5757 слов.
Именно этот список я использую сейчас для создания человекочитаемого пароля.
Использование списка
Вместо того чтобы поместить список слов в список Python, я решил поместить список в БД. Это облегчает добавление дополнительных слов позже, если это будет предпочтительнее.
Модель очень проста.
# code/word_model.py
class Word(models.Model):
word = models.CharField(max_length=10, unique=True)
def __str__(self):
return self.word
Я оставлю на ваше усмотрение, как наполнить модель.
Требования
Пароль должен отвечать следующим требованиям
- В начале и в конце пароля должно быть не менее 1 цифры и не более 2 цифр.
- Разделяйте каждое слово специальным символом.
- Каждое слово будет преобразовано в нижний регистр, верхний регистр или заглавную букву.
- Не повторяйте слова в пароле.
Я также хочу иметь возможность использовать столько слов, сколько захочу, по умолчанию я использую 3 и не допускаю менее двух слов.
Код
# code/password.py
import random
from models import Word
def create_password(words=3):
"""
Generate a password
Parameters
----------
words: int
Returns
-------
str
"""
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
Parameters
----------
amount: int
Returns
-------
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:
continue
selected_word = Word.objects.get(id=id)
if selected_word:
selected_words.append(selected_word.word)
return selected_words
def transform_word(word):
"""
Transform a string
Parameters
----------
word: str
Returns
-------
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
Некоторые примеры паролей, сгенерированных этой функцией:
73PYGMY|Glens~Naiad33
29radar|bylaw%SWATH19
5plant.Cages+Dizzy54
72DOUGH!Yolks-hooch28
92FLESH^churl%genet69
Заключение
Данный код позволяет создавать человекочитаемые пароли. Используемые слова легко расширить, добавив их в базу данных.
Примечания
- Я использую свой выбор специальных символов вместо
string.punctuation
. Мне не нужны были некоторые символы из этого списка, например, кавычки.
Ссылки
- NY Times Wordle: https://www.nytimes.com/games/wordle/index.html
- Стэнфордская база данных GraphBase: https://www-cs-faculty.stanford.edu/~knuth/sgb.html
Нашли опечатку?
Если вы нашли опечатку, предложение, которое можно улучшить, или что-то еще, что следует обновить в этом блоге, вы можете получить доступ к нему через git-репозиторий и сделать запрос на исправление. Вместо того чтобы оставлять комментарий, пожалуйста, зайдите прямо на GitHub и откройте новый запрос на доработку с вашими изменениями.
Фото Колина Ллойда — https://unsplash.com/photos/62OEfKjU1Vs