Как разработать Telegram-бота для генерации сложных паролей

zxj02kgws5o5yh9vw06qmiwagva.png


Чтобы придумать надежный, но запоминающийся пароль, можно взять несколько слов и объединить их в последовательность, напоминающую сюжет. А после — отформатировать и добавить специальные символы. Все просто, но задачу можно автоматизировать — написать Telegram-бота, который будет генерировать пароли прямо в мессенджере. В статье рассказываем, как это сделать.

Требования к работе бота


Прежде чем приступить к написанию кода, определим правила, по которым бот должен работать.

  1. Длина пароля должна быть от 2 до 8 слов. Так мы усложним задачу злоумышленнику — подобрать связку слов намного сложнее, чем одно слово.
  2. Между словами могут быть разделители в виде цифр и спецсимволов. Это увеличит энтропию и затруднит подбор пароля. Пароль с разделителями может выглядеть, например, так: unmovable8ENCRUST=macho.
  3. Дополнительно в пароле могут использоваться спецсимволы в начале (префиксы) и в конце (суффиксы) слова, которые также помогут увеличить сложность подбора.
  4. Количество слов, разделителей, префиксов и суффиксов должно настраиваться пользователем. Пользователю предоставляется интерфейс в виде сообщения с кнопками, нажатием на которые включаются и выключаются отдельные настройки
  5. Пользовательские настройки должны сохраняться в Redis и не сбрасываться при перезагрузке сервера с ботом. Выбор Redis обусловлен тем, что нам не требуются «фичи» реляционных СУБД — схемы, транзакции, миграции и другие — поэтому можно обойтись более простым в развертывании решением. Также мы будем использовать особенность aiogram — механизм конечных автоматов, который нативно поддерживает Redis как бэкенд.


Теперь посмотрим, что понадобится для разработки бота.

Что понадобится для разработки


Перед началом нужно подготовить среду разработки, установить нужные библиотеки и программы, а именно:

  • Python — от версии 3.9 и выше,
  • aiogram — асинхронный фреймворк для работы с Telegram Bot API,
  • Redis — быстрое key-value хранилище,
  • redis-py — клиент для работы с Redis,
  • XKCD-password-generator — библиотека для генерации паролей,
  • pydantic — библиотека для валидации данных и формирования настроек приложения.


И самое главное — репозиторий на GitHub. Его нужно импортировать в свое рабочее окружение и настроить.


icfgqs47gczlbjspzmry5ypqazq.png


Как настроить бота


Запустим бота локально. На этом этапе можем обойтись без Redis, но важно учитывать, что пользовательские настройки не будут сохранены между перезапусками.

Если вы пишете на Python и используете среду разработки PyCharm, то запустить бота будет максимально просто. После клонирования репозитория переключитесь на ветку article-tweaks (git checkout article-tweaks) и создайте новую конфигурацию запуска (Run Configuration). А затем установите параметры:

— BOT_TOKEN — укажите токен бота, его можно получить у @BotFather.

— STORAGE_MODE — выберите memory.

— WORDS__WORDFILE — укажите путь к файлу с набором слов. Он входит в состав репозитория, поэтому отдельно скачивать его не нужно.

Должно получится, как на скриншоте:

zxopesrn88vjb78p0ca691t5ay8.png


После этого запустите созданную конфигурацию. Вы увидите в консоли следующий текст:

INFO:aiogram.dispatcher.dispatcher:Start polling


Если вы используете не PyCharm, то процесс запуска несколько отличается. Создайте виртуальное окружение bot (python3 -m venv bot) и установите зависимости (pip install -r requirements.txt), а после — запустите бота следующей командой:

BOT_TOKEN=ключ от BotFather STORAGE_MODE=memory 
WORDS__WORDFILE=/path/to/words.txt python -m bot


Теперь попробуйте отправить в личные сообщения с ботом команду /start. Если в ответ получили текстовое приветствие, бот работает.

При вводе символа / вы должны увидеть список команд. Попробуйте вызвать их и изучить различные конфигурации. По умолчанию поддерживаются следующие пресеты:

  • /generate_weak — два случайных слова без каких-либо дополнительных символов.
  • /generate_normal — три случайных слова, каждое из которых случайным образом может состоять из всех прописных или всех строчных букв, в качестве разделителей используются числа.
  • /generate_strong — то же, что и в предыдущем случае, но слов четыре, а в качестве разделителей, помимо цифр, возможны спецсимволы.


weofbuingad_lvc8maka07mmkxg.png


При нажатии на команду бот сразу же отправляет в чат сгенерированный пароль.

Кроме этого, есть команда /settings — она приводит к отправке сообщения с настройками. А также команда /generate — отправляет сгенерированный пароль с учетом новой конфигурации:

70ejdebvjopk_eexn2xff3zebb4.png


Деплой бота


Все готово, но есть проблема: бот запущен на компьютере. Это неудобно, если вы хотите обеспечить круглосуточную работу бота. Ведь тогда нужно поддерживать бесперебойную работу компьютера и постоянное соединение с интернетом.

Оптимальное решение проблемы — загрузить проект на облачный сервер с гибкой производительностью ядра. Так можно обеспечить стабильную работу бота и ограничить потребление ресурсов, чтобы не переплачивать.

Поскольку затраты процессора на генерацию пароля и отправку его в Telegram минимальны, нам подойдет сервер линейки Shared Line. Это линейка облачных серверов с возможностью оплаты только части ядра, например 10, 20 или 50%. Shared Line позволяет использовать все преимущества облака и не переплачивать за неиспользуемые ресурсы.

Для начала зарегистрируемся в панели управления и создадим новый сервер в разделе «Облачная платформа». Затем — настроим его.

m55gh6i4vul6zezvt40ncx6azlc.png


Боту подойдет ОС Ubuntu 22.04 LTS, 2 виртуальных ядра с минимальной границей в 10% процессорного времени, 2 ГБ оперативной памяти, а также 10 ГБ на сетевом диске (базовый HDD).

С учетом выделенного IP-адреса такая конфигурация выйдет примерно в 28 ₽/день. При желании можно обойтись без маршрутизируемого IP-адреса, поскольку Telegram-бот может принимать события методом опроса (поллинга), даже находясь за NAT.

После подключения к серверу по SSH, бота необходимо перенести. Для этого выполните следующие шаги:

  1. Откройте консоль сервера и обновите систему с помощью команды:
  2. Создайте отдельного пользователя для нашего бота и добавьте его в группу sudoers:
    mdkigh0eh6jgfmsoaowk4-yi6-c.png

    Дальнейшие действия выполняйте от лица созданного пользователя.
  3. Установите Redis и присоедините его к systemd, воспользовавшись
    удобной инструкцией от DigitalOcean. Шаги 4 и 5 можно пропустить.
  4. Клонируйте репозиторий и переключитесь на нужную ветку:
    kevikwja_udm692qs81l9gcqls0.png

  5. Настройте виртуальное окружение:
    python3 -m venv venv && source /venv/bin/activate && pip install -r requirements.txt

  6. Создайте файл systemd-службы по пути /etc/systemd/system/passgenbot.service со следующим содержимым:
    [Unit]
    Description=Telegram Password Generator Bot
    Requires=redis.service
    After=network.target redis.service
    
    [Service]
    Type=simple
    WorkingDirectory=/home/bot/passgenbot
    ExecStart=/home/bot/passgenbot/venv/bin/python -m bot
    User=bot
    Group=bot
    EnvironmentFile=/home/bot/passgenbot/.env
    KillMode=process
    Restart=always
    RestartSec=10
    
    [Install]
    WantedBy=multi-user.target
    

  7. Обратите внимание на директиву EnvironmentFile. Создайте этот файл и поместите туда необходимые переменные окружения:
  8. Убедитесь, что Redis запущен (systemctl status redis) и включите бота с добавлением его в автозапуск:
    sudo systemctl enable passgenbot --now


Готово!

Разбираемся вместе


Возможности бота можно в любой момент персонализировать под себя. Если в какой-то момент пресетов станет недостаточно — добавить новые или изменить существующие. Это сделать достаточно просто.

За генерацию паролей по заданным пресетам отвечает класс XKCD. Под капотом наш бот выглядит так:

from random import choice
from xkcdpass import xkcd_password

class XKCD:
    # Весь список разделителей, отдельно цифры, отдельно – спецсимволы
    delimiters_numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
    delimiters_full = ["!", "$", "%", "^", "&", "*", "-", "_", "+", "=", ":", "|", "~", "?", "/", ".", ";"] + delimiters_numbers

    def __init__(self, filename: str):
        # Загрузка словаря в память
        self.wordlist = xkcd_password.generate_wordlist(
            wordfile=filename, valid_chars="[a-z]", 
            min_length=4, max_length=10,
        )

    def weak(self):
        # Слабый пароль: 2 слова без раздетилей
        return xkcd_password.generate_xkcdpassword(
               self.wordlist, numwords=2, 
               delimiter="", )

    def normal(self):
        # Средний пароль: 3 слова, разделитель 
        # в виде случайной цифры
        return xkcd_password.generate_xkcdpassword(
            self.wordlist, numwords=3, case="random", random_delimiters=True,
            valid_delimiters=self.delimiters_numbers
        )

    def strong(self):
        # Сильный пароль: 4 слова и большой выбор разделителей  
        return xkcd_password.generate_xkcdpassword(
            self.wordlist, numwords=4, case="random", random_delimiters=True,
            valid_delimiters=self.delimiters_full
        )

    def custom(self, count: int, separators: bool, prefixes: bool):
        # Произвольный пароль: 
        # сложность зависит от настроек пользователя
        pwd = xkcd_password.generate_xkcdpassword(
            self.wordlist, numwords=count, case="random", 
            delimiter="", random_delimiters=separators, 
            valid_delimiters=self.delimiters_full
        )
        if prefixes == separators:
            return pwd
        elif separators and not prefixes:
            return pwd[1:-1]
        elif prefixes and not separators:
            return f"{choice(self.delimiters_full)}{pwd}{choice(self.delimiters_full)}"


bot/pwdgen.py

Для добавлении нового пресета достаточно скопировать существующий, изменить его название и настроить параметры метода generate_xkcdpassword под себя.

И последним этапом — добавить в обработчик commands функцию для вызова своего пресета, чтобы программа знала, в какой ситуации его вызывать. Это можно сделать по аналогии с существующими пресетами.

from aiogram import types, Dispatcher
from aiogram.utils.markdown import hcode
from bot.pwdgen import XKCD

async def cmd_generate_weak(message: types.Message):
    # вызов пресета weak
    pwd: XKCD = message.bot.get("pwd")
    await message.answer(hcode(pwd.weak()))

async def cmd_generate_normal(message: types.Message):
    # вызов пресета normal
    pwd: XKCD = message.bot.get("pwd")
    await message.answer(hcode(pwd.normal()))

async def cmd_generate_strong(message: types.Message):
    # вызов пресета strong
    pwd: XKCD = message.bot.get("pwd")
    await message.answer(hcode(pwd.strong()))

# вот здесь можно добавить свою функцию для вызова пресета

# регистрация команд
def register_commands(dp: Dispatcher):
    # обработчик вызывает пресет weak по команде generate_weak
    dp.register_message_handler(cmd_generate_weak, commands="generate_weak")

    # обработчик вызывает пресет normal по команде generate_normal
    dp.register_message_handler(cmd_generate_normal, commands="generate_normal")

    # обработчик вызывает пресет strong по команде generate_strong
    dp.register_message_handler(cmd_generate_strong, commands="generate_strong")

    # вот здесь можно добавить свою команду


bot/handlers/commands.py

Заключение


Несмотря на то, что разработанный бот работает и выполняет свои прямые задачи по генерации сложных паролей, это далеко не предел его возможностей. В качестве дополнений и улучшений можно реализовать добавление новых языков, автоудаление записей по таймеру, генерацию KeePass-совместимых баз данных, создание нескольких паролей одновременно и другое.

Возможно, эти тексты тоже вас заинтересуют:

→ Сколько стоит содержать виртуальную девушку? Создаем подругу, записывающую кружочки в Telegram, с помощью 4 нейросетей
→ Tinder по интересам, «Морской Boy» и сегментация КТ-снимков: 10 студенческих идей, которые стали проектами
→ Китайская электроника и «запрещенка»: как Поднебесная обходит санкции США, закупая литографические машины

© Habrahabr.ru