
Пароли вне политики
У меня сложилось ощущение, что я уже раз пять писал функцию для генерации паролей. И каждый раз делал это по-разному. А причина тому — различные требования к паролю для разных проектов и инструментов. Здесь не будет сложного кода, просто краткое изложение простого нового решения, которое пришло ко мне вчера.
Начнем с простых требований к паролю:
- должен быть произвольной длины
- должен состоять из любых печатных символов
import string import random from typing import List def generate_password(length: int) -> str: """ Generate a password of a given `length`. """ result: List[str] = [] choices = string.printable # заглавые и строчные буквы, цифры и знаки препинания while len(result) < length: symbol = random.choice(string.printable) result.append(symbol) return "".join(result)
Пробуем:
>>> generate_password(8) ... "1{k]/2)h" >>> generate_password(13) ... "9ar|&:a+U]Il$"
Отлично, задача выполнена, можем смотреть картинки с котиками до конца рабочего дня.
Внезапная политика
Но тут нам говорят, что все не так просто, и пароль для нашей базы данных MyDB должен отвечать некоторым требованиям безопасности. А именно, пароль:
- должен быть не меньше 8 символов
- должен содержать как минимум одну заглавную букву
- должен содержать как минимум одну строчную букву
- должен содержать как минимум одну цифру
- должен содержать как минимум один спец-символ (
!&?и прочие) - а еще он не должен содержать некоторые символы, чтобы не поломать bash-скрипты
Наш код выше для этого не годится, так что придется дорабатывать. И для решения этого я видел целый ворох разных подходов:
- генерируем случайный пароль пока в нем не будет всех нужных символов
- часть пароля генерируем с одними символами, а часть другими, в конец насыпаем спец-символов
- вырезаем из пароля неподдерживаемые символы, в итоге длина не гарантированна
Новый подход
Окей, это уже сложно, так что начнем с функции generate_random_string, которая будет просто генерировать случайные строки из того, что дали.
import string import random from typing import List def generate_random_string(length: int, *choices: str) -> str: """ Generate a string of a given `length`. The result has at least one symbol from each of `choices` if `length` allows. Arguments: length -- Result string length. choices -- Strings with available symbols. """ if not choices: # будем использовать только буквы если нам все равно, из каких символов пароль choices = (string.ascii_letters, ) # создадим строку со всеми доступными символами all_choices = "".join(choices) result: List[str] = [] choice_index = 0 while len(result) < length: # получим по символу из каждого списка, чтобы # каждый список был использован хотя бы один раз if choice_index < len(choices): symbol = random.choice(choices[choice_index]) result.append(symbol) choice_index += 1 continue # а после этого добавляем символы из любого списка symbol = random.choice(all_choices) result.append(symbol) # перемешаем наш результат чтобы распределить начальные символы random.shuffle(result) return "".join(result)
Так, попробуем:
>>> # генерируем строку из цифр >>> generate_random_string(8, string.digits) ... "59197550" >>> # а тут обязательно должен быть восклицательный знак >>> generate_random_string(8, string.ascii_letters, "!") ... "vIOWXN!o"
Отлично, пришло время собственно сгенерировать пароль, отвечающий всем нашим требованиям.
def generate_mydb_password(length: int) -> str: """ Generate a random password for MyDB of a given `length`. The result has at least: - one uppercase letter - one lowercase letter - one digit - one special character Raises: ValueError -- If `length` is lesser than 8. """ if length < 8: raise ValueError("Password length should be at least 8") return generate_random_string( length, string.ascii_uppercase, # в пароле должны быть заглавные буквы string.ascii_lowercase, # и строчные string.digits, # и цифры "!&?", # и спец-символы, добавьте нужных по вкусу )
Осталось только проверить:
>>> generate_mydb_password(8) ... "P?P1&7zL" >>> generate_mydb_password(13) ... "tR!QslK!Sl7EO" >>> generate_mydb_password(2) ... ValueError: Password length should be at least 8
Итого
Мы написали простой в понимании и при этом достаточно случайный генератор паролей, а до конца рабочего дня еще куча времени. Если нет доверия библиотеке random, то можете ее заменить на ту, которая нравится.
Спасибо за внимание!
ссылка на оригинал статьи https://habr.com/ru/post/486414/
Добавить комментарий