Как правильно настроить Webhook для Telegram-бота в 1С при использовании самоподписного сертификата?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Интеграция 1С с мессенджером Telegram через механизмы Webhook является стандартом для создания быстрых и отзывчивых чат-ботов. Однако при использовании самоподписных (self-signed) SSL-сертификатов разработчики часто сталкиваются с трудностями: Telegram либо выдает ошибку SSL routines:certificate verify failed, либо вовсе не может доставить сообщение. В этой статье мы подробно разберем, как правильно сформировать HTTP-запрос из 1С, чтобы Telegram принял ваш сертификат и начал корректно отправлять данные.

В чем заключается основная проблема?

Когда мы работаем с доверенными сертификатами (например, от Let's Encrypt), нам достаточно просто указать URL нашего сервиса. Но в случае с самоподписным сертификатом Telegram требует, чтобы вы загрузили публичную часть этого сертификата (открытый ключ) на их сервер. Это делается через метод setWebhook с использованием типа контента multipart/form-data. Если просто отправить URL без файла, Telegram не сможет проверить подлинность вашего сервера, и в getWebhookInfo вы увидите ошибку проверки сертификата.

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

Подготовка сертификата для Telegram

Прежде чем приступать к написанию кода в 1С, выясним важные технические нюансы, которые предъявляет Telegram к файлам сертификатов:

  1. Формат PEM: Сертификат должен быть представлен строго в формате PEM. Это текстовый файл, который начинается строкой -----BEGIN CERTIFICATE-----.
  2. Common Name (CN): В поле Common Name или Subject Alternative Name сертификата должен быть указан именно тот IP-адрес или домен, который вы используете в url веб-хука. Если в URL указан IP, а в сертификате домен — связь не установится.
  3. Разрешенные порты: Telegram работает только с портами 443, 80, 88 и 8443. Использование других портов для Webhook недопустимо.

Реализация формирования запроса multipart/form-data в 1С

Главная сложность при работе с методом setWebhook заключается в необходимости передать файл сертификата вместе с текстовыми полями. Рассмотрим, как это сделать программно, используя объекты ПотокВПамяти и ЗаписьТекста. Это позволяет нам вручную собрать тело запроса, соблюдая границы (boundary) секций данных.

Разберем по шагам рабочий код для установки Webhook:


&НаСервере
Процедура setWebhookНаСервере(ДанныеСертификата)
    // Генерируем уникальную границу для разделения частей запроса
    Boundary = СтрЗаменить(Строка(Новый УникальныйИдентификатор()), "-", "");
    
    Поток = Новый ПотокВПамяти;
    // Используем кодировку UTF-8, как того требует Telegram API
    ЗаписьТекста = Новый ЗаписьТекста(Поток, КодировкаТекста.UTF8);
    
    // 1. Записываем поле URL
    ЗаписьТекста.ЗаписатьСтроку("--" + Boundary);
    ЗаписьТекста.ЗаписатьСтроку("Content-Disposition: form-data; name=""url"""); 
    ЗаписьТекста.ЗаписатьСтроку("");
    ЗаписьТекста.ЗаписатьСтроку(WebHookURL); // Переменная с адресом вашего HTTP-сервиса
    
    // 2. Если передан сертификат, добавляем его в запрос
    Если ДанныеСертификата <> Неопределено Тогда
        ЗаписьТекста.ЗаписатьСтроку("--" + Boundary); 
        ЗаписьТекста.ЗаписатьСтроку("Content-Disposition: form-data; name=""certificate""; filename=""" + ДанныеСертификата.ИмяФайла + """");
        ЗаписьТекста.ЗаписатьСтроку("");
        
        // Получаем двоичные данные сертификата из хранилища
        ДДСертификата = ПолучитьИзВременногоХранилища(ДанныеСертификата.Адрес);
        ПотокДанныхСертификата = ДДСертификата.ОткрытьПотокДляЧтения();
        
        // Читаем содержимое файла сертификата и записываем его в основной поток
        ЧтениеТекста = Новый ЧтениеТекста(ПотокДанныхСертификата, КодировкаТекста.UTF8);
        ЗаписьТекста.Записать(ЧтениеТекста.Прочитать());
        
        ЧтениеТекста.Закрыть();
        ПотокДанныхСертификата.Закрыть();
    КонецЕсли;
    
    // Завершаем формирование тела запроса финальной границей
    ЗаписьТекста.ЗаписатьСтроку("--" + Boundary + "--");
    ЗаписьТекста.Закрыть();
    
    // Получаем готовое тело запроса в виде двоичных данных
    ТелоЗапроса = Поток.ЗакрытьИПолучитьДвоичныеДанные();
    
    // Настраиваем HTTP-соединение
    ПараметрыПодключения = ПолучитьПараметрыБота(); // Ваша функция для получения токена и хоста
    Соединение = Новый HTTPСоединение(
        "api.telegram.org",
        443, , , ,
        15,
        Новый ЗащищенноеСоединениеOpenSSL()
    );
    
    URLЗапроса = "bot" + ПараметрыПодключения.Token + "/setWebhook";
    
    Запрос = Новый HTTPЗапрос(URLЗапроса);
    // Обязательно указываем Content-Type с нашей границей
    Запрос.Заголовки.Вставить("Content-Type", "multipart/form-data; boundary=" + Boundary);
    Запрос.УстановитьТелоИзДвоичныхДанных(ТелоЗапроса);
    
    Попытка 
        Ответ = Соединение.ОтправитьДляОбработки(Запрос);
        РезультатТекст = Ответ.ПолучитьТелоКакСтроку("UTF-8");
        Сообщить("Ответ Telegram: " + РезультатТекст);
    Исключение
        Сообщить("Ошибка при отправке запроса: " + ОписаниеОшибки());
    КонецПопытки;
КонецПроцедуры

Разбор ключевых моментов реализации

Проанализируем ситуацию: почему мы используем именно такой способ формирования данных? При работе с multipart/form-data крайне важно соблюдать структуру переносов строк. В 1С метод ЗаписатьСтроку() добавляет системный символ перевода строки. При работе на Windows это CRLF, что полностью соответствует спецификации HTTP. Если ваш сервер 1С работает под управлением Linux, стоит убедиться, что используются правильные символы окончания строки, так как Telegram может быть чувствителен к формату LF вместо CRLF.

Обратите внимание на объект ЗащищенноеСоединениеOpenSSL. Мы создаем его без параметров, что позволяет 1С использовать системные настройки для установления защищенного канала с серверами Telegram. Это необходимо для выполнения самого запроса к API.

Безопасность: использование Secret Token

При настройке Webhook настоятельно рекомендуем использовать параметр secret_token. Это произвольная строка, которую вы передаете в методе setWebhook. После этого Telegram будет добавлять её в каждый входящий запрос к вашему HTTP-сервису в заголовке X-Telegram-Bot-Api-Secret-Token.

Рассмотрим, как это защищает вашу базу 1С: так как адрес вашего HTTP-сервиса является публичным, любой злоумышленник, узнав его, может попытаться прислать поддельные данные. Проверяя наличие вашего секретного токена в заголовках запроса внутри 1С, вы гарантируете, что данные пришли именно от Telegram.

Как проверить работоспособность?

После выполнения кода установки выясним, всё ли прошло успешно. Для этого воспользуемся методом getWebhookInfo. Выполните простой GET-запрос к https://api.telegram.org/bot<ВАШ_ТОКЕН>/getWebhookInfo. Проанализируем ответ:

Дополнительные рекомендации по сетевой настройке

Чтобы ваш HTTP-сервис 1С был максимально защищен, мы рекомендуем настроить ваш веб-сервер (Apache или IIS) так, чтобы он принимал входящие соединения только от доверенных IP-адресов Telegram. На текущий момент это подсети 149.154.160.0/20 и 91.108.4.0/22. Это отсечет любые попытки прямого сканирования вашего сервиса из интернета.

Таким образом, правильная настройка Webhook в 1С с использованием самоподписного сертификата требует внимательного отношения к формированию тела запроса и соблюдения технических стандартов Telegram. Используя приведенный выше подход с ручным формированием multipart-запроса, вы сможете стабильно интегрировать чат-ботов даже в закрытые корпоративные контуры.

← На главную