С релизом нового обновления Telegram у многих возникли вопросы как правильно наладить общение бот <-> Telegram
,
бот <-> бэкенд
и сделать это безопасно. Для начала давайте проясним какие способы активации Telegram решил
предоставить пользователю:
@BotFather
.Первый и второй способ предлагают нам аутентифицировать и авторизовать пользователя через специальный объект initData
,
который можно достать с помощью JavaScript. Объект имеет следующую структуру:
{
"query_id": "str",
"user": {
"id": 1,
"is_bot": false,
"first_name": "Pavel",
"last_name": "Durov",
"username": "durov",
"language_code": "ru",
"photo_url": "https://telegram.org/durov.jpg"
},
"auth_date": 1655210062,
"hash": "7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069"
}
Проблема в том, что официальная документация хоть и предупреждает, но не особо объясняет зачем валидировать
поле initData
на сервере бота.
TL;DR
Без валидации:
Мэллори: Привет Боб, я Алиса, дай мне информацию о балансе и сделай перевод на имя Мэллори.
Боб: Пожалуйста, Алиса, всё готово.
С валидацией:
Мэллори: Привет Боб, я Алиса, дай мне информацию о балансе и сделай перевод на имя Мэллори.
Боб: Предоставьте, пожалуйста, валидную подпись Алисы.
Мэллори разводит руками
То есть нужно это затем, чтобы произвести безопасную аутентификацию (проверку подлинности запроса) и авторизацию (понять, что к боту пришла именно Алиса, а не Мэллори).
Без этой валидации бот сможет предоставлять критически важную информацию (например, вы делаете онлайн-банкинг в боте) просто по ID пользователя Telegram.
Важно также понимать, что валидация должна происходить исключительно на серверной части. Валидация на клиентской части мало того, что бессмысленна, так ещё и скомпрометирует токен вашего бота.
Валидация, к слову, не совсем тривиальная. Разработчики Telegram, как обычно, не поленились и вместо проверенного
стандарта JSON Web Token (JWT), реализовали свой собственный велосипед, да ещё и на базе обычного HMAC-SHA256
(то есть HS256 будь у нас JWT-токен). В результате initData
представляет собой URL-encoded
строку query-параметров. Для корректной валидации которой требуется следующая цепочка шагов:
user
останется не декодированным).hash
auth_date=<auth_date>\nquery_id=<query_id>\nuser=<user>
. Важно сохранить значение с ключом user
в чистом JSON.WebAppData
.hash
.По аналогии с JWT, если валидация прошла успешно, пользователя можно считать аутентифицированным
и переходить к авторизации с помощью предоставленного payload (в нашем случае это id
в объекте user
).
Говоря о query_id
, который мы получаем в initData
. Используя это поле, мы можем создать в чате сообщение с ботом
от имени пользователя с бейджем via @your_bot
. Для этого потребуется вызвать метод answerWebAppQuery.
initData
по факту является полноценным stateless-токеном по типу JWTvia @your_bot
initData
и работы с пользователем
С первым и вторым способом всё понятно: вы получаете от Telegram подобие готового токена и поэтому реализация собственной аутентификации и авторизации не требуется, требуется только валидация.
Но с третьим способом ситуация с одной стороны проще, с другой сложней. Дело в том, что initData
не приходит
и наладить общение с серверной частью не выйдет. Ведь вы не будете знать кто к вам пришёл.
Однако при использовании этого способа появляется возможность использовать метод Telegram.WebApp.sendData()
,
который позволяет отправить сообщение боту напрямую, а тот предоставит его вам через long-polling или вебхуки.
Стоит учесть, что после успешного выполнения веб-окно автоматически закроется, а бот отрапортует сервисным сообщением
Вы успешно передали данные боту кнопкой «Test button».
Поэтому Telegram позиционирует этот способ как удобный способ сделать гибкую веб-форму ввода с полями
типа date picker
. Вернуть значения формы можно с помощью метода Telegram.WebApp.sendData()
.
Нужно понимать, что в JS-файле этот метод является лишь прослойкой,
само значение, переданное в sendData()
отправляются далее через
MTProto-метод
sendWebAppData
. Методы MTProto невозможно использовать без авторизации в Telegram, поэтому тут мессенджер берет
безопасность полностью на себя.
В этом заключается плюс этого метода.
initData
не приходит, возможность авторизовать пользователя на своём бэкенде (даже если он есть) отсутствуетЕсть также четвертый способ, который технически не отличается от первого и второго
(только дополнительными полями в initData
), но в этом случае бот добавляется
в меню вложений.
К сожалению эта возможность пока что доступна только для тех, кто участвует в рекламной платформе Telegram и, следовательно, внёс залог 2 миллиона евро. Поэтому пока что говорить тут особо не о чем. Из известных мне публичных ботов такую интеграцию использует @wallet.
Основной плюс — возможность использовать бота не только в личной переписке с ботом. Например, вышеупомянутый @wallet позволяет отправить любому собеседнику сообщение с информацией о переводе, собеседнику остается лишь нажать и получить свой перевод.