Уверен, многие даже не задумываются над тем, как поведёт себя разрабатываемый бот в случае неправильного его использования. Самое время вскрыть своих ботов и исправить часто допускаемые недочёты, которые я здесь опишу. Снова их 5, снова никакого кода, снова только описание. Добро пожаловать под кат.
Предыдущие части:
1. Русские не просто так в аутсайдерах
Извините, если пересеклось с повесткой, но так уж получилось, что международная кодировка использует однобайтовую часть для латиницы, и двухбайтовую — для кириллицы и прочих (включая эмодзи). А значит при использовании «не латиницы», например, в кнопках (параметр callback_data) или в тексте сообщения (или в подписи к медиа) вам просто-напросто может не хватить места. Если отправить такой «длинный» запрос на сервер телеграма, он скажет об этом, но запрос дальше никуда не пойдёт. Ваша задача — проверить ответ от сервера, чтобы результат был положительным (например, для метода sendMessage
положительным результатом будет отправленное сообщение, для answerCallbackQuery
— True, а для editMessageText
вообще по-разному, в зависимости от типа редактируемого сообщения). Если результат не положительный — нужно это как-то решать: сократить сообщение или ещё что-то.
Программист обязательно должен сделать так, чтобы его код сам себя контролировал. Ответ каждого вызываемого метода должен быть проверен, вдруг он не выполнился — тогда нужно что-то предпринять, чтобы код выполнился до конца, а не оборвался на середине из-за неверного ответа или неоднозначного поведения.
2. Пользователи имеют возможность удалять сообщения
Будет печально, если вы отправите сообщение, которое нужно будет потом обновить, но при этом сообщения в нужный момент не окажется в диалоге.
Не стоит забывать, что для программиста является заповедью считать пользователя идиотом.
Как бы вам ни хотелось писать поменьше проверок, этого не добиться, пока с другой стороны находится человек. Очевидно, что не выйдет обновить несуществующее сообщение, но есть проблема посерьёзнее: кнопки, крепящиеся под клавиатуру (ReplyKeyboard), существуют до тех пор (если их специально не удалять), пока существует сообщение, с которым эти кнопки пришли к пользователю. Поэтому если человек удаляет сообщение, эти кнопки быстрого набора также исчезают.
Я не пользуюсь этими кнопками, поэтому не могу привести примеры, когда это критично, но учитывайте то, что человек может их вот так вот потерять — просто удалив «то самое» сообщение.
3. Нужно больше проверок, чем вы думаете
Помимо базового «не допускайте SQL-инъекций» есть очень забавная ситуация:
Вот как выглядит моё имя в настройках:
Если разработчик использует html-разметку в сообщениях, то любые теги не будут являться текстом, если их специально не экранировать. Я не знаю, как это работает с markdown-разметкой, но имейте в виду, что при неправильной html-разметке (неполные теги, несуществующие и т. п.) телеграм не отправит сообщение в чат, а скажет, что у вас неверная разметка, а вы потом будете гадать, что же не так работает.
4. Нет, на этом проверки не заканчиваются
Бывает и такое:
Пользователь может дважды использовать кнопку (иногда даже очень опасную как для разработчика, так и для пользователя) в одинаковых сообщениях. Если вы не предусмотрели ещё кучу проверок на нажатие кнопки — результат может оказаться печальным. Иногда даже одной кнопки хватает, чтобы положить вам сервер или заставить его работать неправильно. Для этого есть несколько решений. Если несколько раз нажатая кнопка доставит проблемы, можно запретить нажимать повторно на кнопку до тех пор, пока не обработается текущее нажатие: например, выставить пользователю флаг блокировки, чтобы любые (или некоторые) запросы от пользователя игнорировались, пока предыдущий запрос обрабатывается. Помимо проверки на повторное использование опасных кнопок на сервере можно добавлять к кнопкам секрет (или, чтобы сэкономить место в запросе, сохранять идентификатор отправленного сообщения с нужными кнопками, они потом возвращаются к нам при нажатии), и при нажатии проверять, та ли (т. е. актуальная ли) кнопка используется. Если нет, то делать, например, вот так:
5. Телеграму тоже нельзя доверять
Так как при установке вебхука вы используете прямую ссылку на скрипт, это значит, что кто угодно может отправить любой запрос вашему боту, если ссылка будет скомпрометирована. Опасность отсутствия проверки данных, полученных от телеграма, прямо пропорциональна серьёзности разработки: чем «вкуснее» ваш бот (или ваша деятельность, соответственно) для хакеров, тем выше вероятность взлома, если вы не проверяете данные, полученные извне (даже если это сам телеграм). Если ожидается число — проверьте, что это действительно число; если ожидается текст с определённым количеством символов, пробелов и т.п. — проверьте и это.
Да, в простых ботах этого всего не требуется, но если вы будете следить за этими мелочами, контролировать сами себя, учитывать любой исход событий, тогда вам придётся меньше времени тратить на отлов ошибок. Создавайте ботов так, чтобы даже при намеренном неправильном использовании нельзя было сломать их поведение. Если не хотите продумывать это — просто запретите использовать бота неправильно.
Комментарии (17)
pae174
14.12.2023 14:41Так как при установке вебхука вы используете прямую ссылку на скрипт, это значит, что кто угодно может отправить любой запрос вашему боту, если ссылка будет скомпрометирована.
Там разве нельзя отсеять посторонних по IP адресам серверов Телеграма?
r_anisimov Автор
14.12.2023 14:41Не знаю. А телеграм не использует прокси CloudFlare или типа того? У них нет вроде списка адресов.
beatleboy
14.12.2023 14:41У метода setWebhook есть поле secret_token. Для безопасности можно например раз в сутки менять секретный токен.
Также адрес для обработки вебхука у вас должен быть не простым, с каким нибудь хэшом в url (его тоже можно периодически менять)
Ну и адреса подсетей телеги тоже известны:Accepts incoming POSTs from subnets
149.154.160.0/20
and91.108.4.0/22
on port 443, 80, 88, or 8443.
r_anisimov Автор
14.12.2023 14:41Про адреса не знал. Не изучал вопрос настолько глубоко, ибо не нужно. Про секретный токен напишу в следующей статье. Во всяком случае, суть в том, что почти никому не требуется настолько тщательная защита от внедрения.
pavelmakis
14.12.2023 14:41Удивительно, как вы затрагиваете тему безопасности вебхуков, про токен не упомянули, а про адреса вовсе не знали, потому что "ибо не нужно". Вы уверены что стоит продолжать составлять подобные "правила" хороших телеграмм ботов? Может вы не обладаете достаточными компетенциями?
r_anisimov Автор
14.12.2023 14:41Уверен. Если бы телеграм-боты были нормальными, а не вырвиглазными и плохо работающими, статьи бы не понадобились. Изначально я пишу про хорошее проектирование, а не безопасность, поэтому она упомянута вскользь.
pae174
14.12.2023 14:41У CloudFlare как раз есть список IP адресов. Кроме того там есть ещё и authenticated origin pull, защищающий от MITM между CF и вашим сервером.
Как уже выше прокомментировали, IP телеграма известны, а значит их просто можно забить в фаерволл и не париться по поводу посторонних запросов. Остается только возможность атаки со спуфингом IP, для предотвращения которой у CF есть authenticated origin pull а у телеграма ничего нет, но эта вероятность реально остается только у спецслужб или чего-то такого.
sshmakov
Кнопки стоит удалять из сообщения после их нажатия, оставлять только актуальные, заодно и чат будет аккуратнее выглядеть.
r_anisimov Автор
Поясните, пожалуйста, что имеете в виду.
sshmakov
Когда человек нажал какую-то кнопку под сообщением, то надо все кнопки под этим сообщением скрыть (если, конечно, вы не хотите их оставлять для чего-то). Телега сама кнопки не скрывает, это делается ботом через апдейт сообщения.
Если в чат отправляется сообщение с новыми кнопками, к примеру, как на вашем экране с двумя start, то стоит почистить кнопки у предыдущих сообщений.
r_anisimov Автор
Если отправлена команда, чтобы найти предыдущее сообщение с кнопками, нужно хранить его ID в базе. Неэффективно. Плюс само сообщение без кнопок будет выглядеть неполноценно, что портит интерфейс.
pavelmakis
Не нужно ничего хранить. После нажатия кнопки пользователем, можно вызвать метод очистки клавиатуры этого же сообщения, если она дальше не нужна
r_anisimov Автор
Подождите. А зачем скрывать кнопки и отправлять новое сообщение? Это решение ещё хуже, так остаётся висячее сообщение. Кнопки должны обновляться, никак иначе. Либо я не понимаю, о чём речь.
pavelmakis
Речь была про ситуацию когда кнопки не нужны, но они остались после использования. Чтобы они не "висели" ненужными их лучше убирать. Понятно что обновлять их лучше чем слать новое сообщение.
r_anisimov Автор
В статье я описал повторное сообщение после отправки команды. Так предыдущее сообщение не узнать, не сохранив ID.