При разработке собственных HTTP-сервисов на платформе 1С программисты часто сталкиваются с необходимостью идентификации клиента по его IP-адресу. Это может быть нужно для логирования, ограничения доступа по белому списку или привязки запроса к конкретному рабочему месту в локальной сети. Однако штатные средства встроенного языка 1С не предоставляют прямой функции вроде ПолучитьIPАдресЗапроса(). Это связано с архитектурными особенностями: 1С работает не напрямую с сетевым сокетом, а через прослойку в виде веб-сервера (Apache или IIS) и модуля расширения веб-сервера.
В этой статье мы подробно рассмотрим, как обойти это ограничение, правильно настроить веб-сервер и извлечь заветный IP-адрес из структуры запроса.
Давайте проанализируем путь прохождения запроса. Когда внешнее устройство отправляет HTTP-пакет, его первым встречает веб-сервер. Веб-сервер обрабатывает запрос и передает его модулю расширения 1С (wsap24.dll для Apache или isapi_1c.dll для IIS). Для 1С "клиентом" технически является сам веб-сервер или локальный процесс. Именно поэтому стандартные методы получения соединений часто возвращают 127.0.0.1 или адрес сервера приложений.
Чтобы решить задачу, нам нужно заставить веб-сервер "пробросить" адрес реального клиента внутрь 1С в виде HTTP-заголовка.
Если ваша публикация работает под управлением Apache, мы можем использовать модуль преобразования ссылок mod_rewrite для создания пользовательского заголовка. Рассмотрим по шагам, как это сделать в конфигурационном файле httpd.conf или в файле описания публикации .htaccess.
Нам необходимо проверить, не пришел ли запрос через прокси-сервер (в таком случае адрес будет в заголовке X-Forwarded-For), и если нет — взять системную переменную REMOTE_ADDR. Проанализируем пример настройки:
# Включаем механизм преобразования
RewriteEngine On
# Если заголовок X-Forwarded-For пуст, создаем переменную RA со значением IP-клиента
RewriteCond %{HTTP:X-Forwarded-For} =""
RewriteRule .* - [E=RA:%{REMOTE_ADDR}]
# Если X-Forwarded-For не пуст, вырезаем из него первый IP (самый первый клиент в цепочке)
RewriteCond %{HTTP:X-Forwarded-For} !=""
RewriteCond %{HTTP:X-Forwarded-For} "(([0-9]{1,3}\.){3}[0-9]{1,3})"
RewriteRule .* - [E=RA:%1]
# Устанавливаем итоговый заголовок, который будет виден в 1С
RequestHeader set X-Custom-Remote-Addr "%{RA}e" env=RA
После такой настройки в коде 1С мы сможем легко получить значение этого заголовка. Рассмотрим этот процесс далее.
Для веб-сервера IIS ситуация аналогична. Нам потребуется установленный модуль URL Rewrite. Рассмотрим алгоритм действий:
HTTP_X_REAL_IP.{REMOTE_ADDR}.В результате IIS начнет добавлять к каждому запросу заголовок X-Real-IP, который станет доступен внутри платформы 1С.
Когда веб-сервер настроен, нам остается только корректно прочитать данные в методе-обработчике HTTP-сервиса. Объект HTTPСервисЗапрос содержит свойство Заголовки, которое представляет собой соответствие (ключ-значение).
Разберем пример кода на стороне 1С:
Функция ЛюбойМетодГет(Запрос)
// Пытаемся получить адрес из разных возможных заголовков
IPАдрес = Запрос.Заголовки.Получить("X-Custom-Remote-Addr");
Если IPАдрес = Неопределено Тогда
// Проверяем стандартный заголовок для прокси-серверов (Nginx и др.)
IPАдрес = Запрос.Заголовки.Получить("X-Forwarded-For");
КонецЕсли;
Если IPАдрес = Неопределено Тогда
// Проверяем еще один распространенный вариант
IPАдрес = Запрос.Заголовки.Получить("X-Real-IP");
КонецЕсли;
Если IPАдрес <> Неопределено Тогда
// Если в заголовке список IP через запятую, берем первый
ПозицияЗапятой = Найти(IPАдрес, ",");
Если ПозицияЗапятой > 0 Тогда
IPАдрес = СокрЛП(Лев(IPАдрес, ПозицияЗапятой - 1));
КонецЕсли;
ЗаписьЖурналаРегистрации("HTTP.Запрос", УровеньЖурналаРегистрации.Информация, , , "Запрос от IP: " + IPАдрес);
Иначе
ЗаписьЖурналаРегистрации("HTTP.Запрос", УровеньЖурналаРегистрации.Предупреждение, , , "Не удалось определить IP отправителя");
КонецЕсли;
// Далее логика обработки запроса...
Ответ = Новый HTTPСервисОтвет(200);
Возврат Ответ;
КонецФункции
Если у нас нет доступа к настройкам веб-сервера (например, при использовании облачного хостинга), мы можем использовать "клиентский" подход. В этом случае устройство, отправляющее запрос, должно само определить свой IP и передать его в параметрах или теле запроса.
Рассмотрим ситуацию, когда клиентом выступает другая база 1С. Мы можем программно узнать внешний IP клиента, используя публичные API-сервисы (например, 2ip.ua). Проанализируем функцию получения внешнего IP:
Функция ПолучитьВнешнийIP() Экспорт
Результат = "";
Попытка
Соединение = Новый HTTPСоединение("api.2ip.ua",,,,,,Новый ЗащищенноеСоединениеOpenSSL);
Запрос = Новый HTTPЗапрос("/provider.json?ip=");
Ответ = Соединение.Получить(Запрос);
Если Ответ.КодСостояния = 200 Тогда
ТекстJSON = Ответ.ПолучитьТелоКакСтроку();
// Парсим простейшим способом или через ЧтениеJSON
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(ТекстJSON);
Данные = ПрочитатьJSON(Чтение);
Результат = Данные.ip;
Чтение.Закрыть();
КонецЕсли;
Исключение
// Обработка ошибки доступа к интернету
КонецПопытки;
Возврат Результат;
КонецФункции
После получения этого адреса клиент добавляет его к своему основному запросу:
МойIP = ПолучитьВнешнийIP();
ЗапросКСервису = Новый HTTPЗапрос("/hs/MyService/v1/data?clientIP=" + МойIP);
// Отправка...
Проанализируем риски использования заголовка X-Forwarded-For. Важно понимать, что любой пользователь может вручную подставить этот заголовок в свой запрос (так называемый IP Spoofing). Если ваша логика безопасности строится на проверке IP, никогда не доверяйте этим данным безусловно, если запрос пришел из открытой сети.
Рекомендация: На стороне 1С проверяйте IP-адрес только в том случае, если вы уверены, что ваш веб-сервер или прокси-сервер (например, Nginx) жестко перезаписывает эти заголовки, удаляя те, что прислал клиент.
Мы рассмотрели несколько подходов к решению задачи. Выясним, какой из них лучше:
Используя приведенные выше инструкции и примеры кода, вы сможете эффективно реализовать механизм определения местоположения и идентификации отправителей в ваших HTTP-сервисах на платформе 1С.