Почему POST-запрос с заголовками авторизации работает в PHP cURL, но возвращает ошибку в 1С?

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

При интеграции 1С со сторонними веб-сервисами часто возникает ситуация: пример вызова API на PHP через библиотеку cURL отрабатывает корректно, а аналогичный код на 1С с использованием объекта HTTPСоединение возвращает ошибку авторизации или некорректный ответ от сервера. Разберем по шагам, в чем заключаются ключевые отличия формирования HTTP-запросов в PHP и 1С, как локализовать проблему и какими способами её можно решить.

Анализ различий между PHP cURL и HTTPСоединение в 1С

Рассмотрим стандартный рабочий пример на PHP:


$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; PHP client; ' . php_uname('s') . '; PHP/' . phpversion() . ')');
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Sign: ' . $sign,
    'Key: ' . $key,
));
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$res = curl_exec($ch);

При переносе этого кода на 1С разработчики часто создают соответствие заголовков и отправляют запрос через HTTPЗапрос:


ssl = Новый ЗащищенноеСоединениеOpenSSL();
Соединение = Новый HTTPСоединение(ПутьКСайту,,,,,, ssl);
Заголовки = Новый Соответствие;
Заголовки.Вставить("Sign", ШифрованноеЗначение);
Заголовки.Вставить("Key", Ключ);
Запрос = Новый HTTPЗапрос(ЗапросКСайту, Заголовки);
Результат = Соединение.ОтправитьДляОбработки(Запрос);
СтрокаОтвет = Результат.ПолучитьТелоКакСтроку();

Выясним причину, почему аналогичный по структуре код на платформе 1С может приводить к ошибкам со стороны веб-сервера:

  1. Заголовок User-Agent: В PHP cURL заголовок клиента задается явно или подставляется автоматически. В 1С по умолчанию отправляется системный User-Agent вида 1C+Enterprise/8.3... либо он отсутствует. Многие современные веб-сервисы, Cloudflare или WAF блокируют запросы со стандартными служебными клиентами, возвращая ошибку авторизации.
  2. Тело запроса и заголовок Content-Type: Опция CURLOPT_POSTFIELDS в PHP не только передает данные, но и автоматически выставляет заголовок Content-Type (например, application/x-www-form-urlencoded). В 1С метод ОтправитьДляОбработки() отправляет тело, только если оно явно установлено у объекта HTTPЗапрос.
  3. Порядок байтов (BOM) в теле запроса: Если тело запроса устанавливается методом УстановитьТелоИзСтроки() без указания параметров BOM, платформа может добавить маркер последовательности байтов UTF-8, из-за чего подпись (Sign/HMAC) на сервере не сойдется.
  4. Особенности реализации SSL/TLS: На более ранних версиях платформы (например, 8.3.10) встроенная библиотека OpenSSL в ЗащищенноеСоединениеOpenSSL может не согласовывать определенные шифры или версии протоколов TLS.

Способ 1. Корректная настройка штатного объекта HTTPЗапрос

Разберем, как правильно сформировать запрос средствами платформы 1С, добавив все необходимые служебные заголовки и отключив BOM при установке тела запроса.


ssl = Новый ЗащищенноеСоединениеOpenSSL();
Соединение = Новый HTTPСоединение("api.example.com", 443,,,, 30, ssl);

Заголовки = Новый Соответствие;
Заголовки.Вставить("Sign", ШифрованноеЗначение);
Заголовки.Вставить("Key", Ключ);
// Обязательно указываем User-Agent, идентичный браузеру или PHP-клиенту
Заголовки.Вставить("User-Agent", "Mozilla/4.0 (compatible; 1C client; Windows NT)");
Заголовки.Вставить("Content-Type", "application/x-www-form-urlencoded");

Запрос = Новый HTTPЗапрос("/api/v1/method", Заголовки);

// Если передаются данные формы или JSON, исключаем BOM
Запрос.УстановитьТелоИзСтроки(СтрокаТелаЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);

Результат = Соединение.ОтправитьДляОбработки(Запрос);
СтрокаОтвет = Результат.ПолучитьТелоКакСтроку();

Способ 2. Использование системного COM-объекта WinHttpRequest

Если сервер отклоняет запросы из-за ограничений OpenSSL в конкретной версии платформы 1С или нестандартной обработки SSL-рукопожатия, надежным решением под Windows является использование объекта WinHttp.WinHttpRequest.5.1. Он задействует системный стек операционной системы Windows (Schannel), который автоматически поддерживает актуальные версии TLS и сертификаты.

Рассмотрим пример отправки POST-запроса через WinHttpRequest:


Попытка
    HTTPЗапросCOM = Новый COMОбъект("WinHttp.WinHttpRequest.5.1");
    HTTPЗапросCOM.Open("POST", "https://api.example.com/api/v1/method", Ложь);
    
    // Установка заголовков
    HTTPЗапросCOM.SetRequestHeader("Sign", ШифрованноеЗначение);
    HTTPЗапросCOM.SetRequestHeader("Key", Ключ);
    HTTPЗапросCOM.SetRequestHeader("User-Agent", "Mozilla/4.0 (compatible; WinHttp client)");
    HTTPЗапросCOM.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    
    // Отключение проверки сертификатов при необходимости (аналог CURLOPT_SSL_VERIFYPEER = false)
    // 0x0100 - игнорировать неизвестный УЦ, 0x2000 - игнорировать несоответствие имени узла
    HTTPЗапросCOM.Option(4, 16896); // 0x4200 (0x0100 + 0x2000 + 0x0200 + 0x1000)
    
    HTTPЗапросCOM.Send(СтрокаТелаЗапроса);
    
    КодОтвета = HTTPЗапросCOM.Status;
    СтрокаОтвет = HTTPЗапросCOM.ResponseText;
Исключение
    Сообщить("Ошибка отправки запроса: " + ОписаниеОшибки());
КонецПопытки;

Методы диагностики и отладки HTTP-запросов

Проанализируем инструменты, которые позволяют быстро найти различия между запросами из PHP и 1С:

  1. Эхо-сервисы (httpbin.org): Отправьте GET или POST запрос из PHP и из 1С на тестовый эндпоинт https://httpbin.org/anything или https://httpbin.org/headers. В ответе сервер вернет полный список заголовков и структуру тела, которые до него дошли. Сравнив оба ответа, можно мгновенно увидеть отсутствующий или искаженный заголовок.
  2. Локальные прокси-отладчики (Fiddler / Charles): Запуск локального сниффера позволяет перехватить трафик как от cURL, так и от процесса 1cv8.exe, предоставляя точную побайтовую картину отправляемых HTTP-пакетов.
← На главную