Уверен, многие даже не задумываются над тем, как поведёт себя разрабатываемый бот в случае неправильного его использования. Самое время вскрыть своих ботов и исправить часто допускаемые недочёты, которые я здесь опишу. Снова их 5, снова никакого кода, снова только описание. Добро пожаловать под кат.


Предыдущие части:


1. Русские не просто так в аутсайдерах

Извините, если пересеклось с повесткой, но так уж получилось, что международная кодировка использует однобайтовую часть для латиницы, и двухбайтовую — для кириллицы и прочих (включая эмодзи). А значит при использовании «не латиницы», например, в кнопках (параметр callback_data) или в тексте сообщения (или в подписи к медиа) вам просто-напросто может не хватить места. Если отправить такой «длинный» запрос на сервер телеграма, он скажет об этом, но запрос дальше никуда не пойдёт. Ваша задача — проверить ответ от сервера, чтобы результат был положительным (например, для метода sendMessage положительным результатом будет отправленное сообщение, для answerCallbackQueryTrue, а для editMessageText вообще по-разному, в зависимости от типа редактируемого сообщения). Если результат не положительный — нужно это как-то решать: сократить сообщение или ещё что-то.

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

2. Пользователи имеют возможность удалять сообщения

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

Не стоит забывать, что для программиста является заповедью считать пользователя идиотом.

Как бы вам ни хотелось писать поменьше проверок, этого не добиться, пока с другой стороны находится человек. Очевидно, что не выйдет обновить несуществующее сообщение, но есть проблема посерьёзнее: кнопки, крепящиеся под клавиатуру (ReplyKeyboard), существуют до тех пор (если их специально не удалять), пока существует сообщение, с которым эти кнопки пришли к пользователю. Поэтому если человек удаляет сообщение, эти кнопки быстрого набора также исчезают.

Пример кнопок, пришедших с сообщением от бота.
Пример кнопок, пришедших с сообщением от бота.

Я не пользуюсь этими кнопками, поэтому не могу привести примеры, когда это критично, но учитывайте то, что человек может их вот так вот потерять — просто удалив «то самое» сообщение.

3. Нужно больше проверок, чем вы думаете

Помимо базового «не допускайте SQL-инъекций» есть очень забавная ситуация:

Попросил бота отправить мне моё имя.
Попросил бота отправить мне моё имя.

Вот как выглядит моё имя в настройках:

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

Если разработчик использует html-разметку в сообщениях, то любые теги не будут являться текстом, если их специально не экранировать. Я не знаю, как это работает с markdown-разметкой, но имейте в виду, что при неправильной html-разметке (неполные теги, несуществующие и т. п.) телеграм не отправит сообщение в чат, а скажет, что у вас неверная разметка, а вы потом будете гадать, что же не так работает.

4. Нет, на этом проверки не заканчиваются

Бывает и такое:

Один из моих ботов. Два одинаковых сообщения.
Один из моих ботов. Два одинаковых сообщения.

Пользователь может дважды использовать кнопку (иногда даже очень опасную как для разработчика, так и для пользователя) в одинаковых сообщениях. Если вы не предусмотрели ещё кучу проверок на нажатие кнопки — результат может оказаться печальным. Иногда даже одной кнопки хватает, чтобы положить вам сервер или заставить его работать неправильно. Для этого есть несколько решений. Если несколько раз нажатая кнопка доставит проблемы, можно запретить нажимать повторно на кнопку до тех пор, пока не обработается текущее нажатие: например, выставить пользователю флаг блокировки, чтобы любые (или некоторые) запросы от пользователя игнорировались, пока предыдущий запрос обрабатывается. Помимо проверки на повторное использование опасных кнопок на сервере можно добавлять к кнопкам секрет (или, чтобы сэкономить место в запросе, сохранять идентификатор отправленного сообщения с нужными кнопками, они потом возвращаются к нам при нажатии), и при нажатии проверять, та ли (т. е. актуальная ли) кнопка используется. Если нет, то делать, например, вот так:

Пример отказа от обработки устаревшего сообщения.
Пример отказа от обработки устаревшего сообщения.

5. Телеграму тоже нельзя доверять

Так как при установке вебхука вы используете прямую ссылку на скрипт, это значит, что кто угодно может отправить любой запрос вашему боту, если ссылка будет скомпрометирована. Опасность отсутствия проверки данных, полученных от телеграма, прямо пропорциональна серьёзности разработки: чем «вкуснее» ваш бот (или ваша деятельность, соответственно) для хакеров, тем выше вероятность взлома, если вы не проверяете данные, полученные извне (даже если это сам телеграм). Если ожидается число — проверьте, что это действительно число; если ожидается текст с определённым количеством символов, пробелов и т.п. — проверьте и это.


Да, в простых ботах этого всего не требуется, но если вы будете следить за этими мелочами, контролировать сами себя, учитывать любой исход событий, тогда вам придётся меньше времени тратить на отлов ошибок. Создавайте ботов так, чтобы даже при намеренном неправильном использовании нельзя было сломать их поведение. Если не хотите продумывать это — просто запретите использовать бота неправильно.

Комментарии (17)


  1. sshmakov
    14.12.2023 14:41

    Кнопки стоит удалять из сообщения после их нажатия, оставлять только актуальные, заодно и чат будет аккуратнее выглядеть.


    1. r_anisimov Автор
      14.12.2023 14:41

      Поясните, пожалуйста, что имеете в виду.


      1. sshmakov
        14.12.2023 14:41

        Когда человек нажал какую-то кнопку под сообщением, то надо все кнопки под этим сообщением скрыть (если, конечно, вы не хотите их оставлять для чего-то). Телега сама кнопки не скрывает, это делается ботом через апдейт сообщения.

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


        1. r_anisimov Автор
          14.12.2023 14:41

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


          1. pavelmakis
            14.12.2023 14:41

            Не нужно ничего хранить. После нажатия кнопки пользователем, можно вызвать метод очистки клавиатуры этого же сообщения, если она дальше не нужна


            1. r_anisimov Автор
              14.12.2023 14:41

              Подождите. А зачем скрывать кнопки и отправлять новое сообщение? Это решение ещё хуже, так остаётся висячее сообщение. Кнопки должны обновляться, никак иначе. Либо я не понимаю, о чём речь.


              1. pavelmakis
                14.12.2023 14:41

                Речь была про ситуацию когда кнопки не нужны, но они остались после использования. Чтобы они не "висели" ненужными их лучше убирать. Понятно что обновлять их лучше чем слать новое сообщение.


                1. r_anisimov Автор
                  14.12.2023 14:41

                  В статье я описал повторное сообщение после отправки команды. Так предыдущее сообщение не узнать, не сохранив ID.


  1. pae174
    14.12.2023 14:41

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

    Там разве нельзя отсеять посторонних по IP адресам серверов Телеграма?


    1. r_anisimov Автор
      14.12.2023 14:41

      Не знаю. А телеграм не использует прокси CloudFlare или типа того? У них нет вроде списка адресов.


      1. beatleboy
        14.12.2023 14:41

        У метода setWebhook есть поле secret_token. Для безопасности можно например раз в сутки менять секретный токен.
        Также адрес для обработки вебхука у вас должен быть не простым, с каким нибудь хэшом в url (его тоже можно периодически менять)

        Ну и адреса подсетей телеги тоже известны:

        • Accepts incoming POSTs from subnets 149.154.160.0/20 and 91.108.4.0/22 on port 443, 80, 88, or 8443.


        1. r_anisimov Автор
          14.12.2023 14:41

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


          1. pavelmakis
            14.12.2023 14:41

            Удивительно, как вы затрагиваете тему безопасности вебхуков, про токен не упомянули, а про адреса вовсе не знали, потому что "ибо не нужно". Вы уверены что стоит продолжать составлять подобные "правила" хороших телеграмм ботов? Может вы не обладаете достаточными компетенциями?


            1. r_anisimov Автор
              14.12.2023 14:41

              Уверен. Если бы телеграм-боты были нормальными, а не вырвиглазными и плохо работающими, статьи бы не понадобились. Изначально я пишу про хорошее проектирование, а не безопасность, поэтому она упомянута вскользь.


              1. needsomedata
                14.12.2023 14:41

                Вообще уже есть отличные конструкторы скриптовых ботов, чем они плохи?


                1. r_anisimov Автор
                  14.12.2023 14:41

                  Не знаю, не пользовался.


      1. pae174
        14.12.2023 14:41

        У CloudFlare как раз есть список IP адресов. Кроме того там есть ещё и authenticated origin pull, защищающий от MITM между CF и вашим сервером.

        Как уже выше прокомментировали, IP телеграма известны, а значит их просто можно забить в фаерволл и не париться по поводу посторонних запросов. Остается только возможность атаки со спуфингом IP, для предотвращения которой у CF есть authenticated origin pull а у телеграма ничего нет, но эта вероятность реально остается только у спецслужб или чего-то такого.