При разработке тиражных решений или выполнении крупных проектов локализации на иностранные языки (например, внедрении английского, испанского или китайского интерфейса) разработчики вкладывают колоссальные ресурсы в перевод конфигурации. Однако стандартный механизм локализации 1С:Предприятия устроен так, что все переводы хранятся в метаданных в виде синонимов объектов. Любой пользователь, имеющий доступ к Конфигуратору, может буквально в два клика открыть инструмент Редактирование текстов интерфейса и выгрузить весь ваш многомесячный труд в один файл.
В этой статье мы подробно разберем, как защитить результаты своего труда. Мы проанализируем различные подходы — от простых организационных мер до сложных программных архитектур с динамическим переводом «на лету» с использованием расширений, обфускации и внешних DRM-систем.
Прежде чем перейти к методам защиты, выясним причину уязвимости. Когда мы добавляем в конфигурацию новый язык (например, английский en), платформа предлагает нам заполнять синонимы объектов, заголовки реквизитов и элементов форм для каждого языка отдельно. Все эти данные компилируются в метаданные конфигурации.
Главный недостаток такого подхода — отсутствие модульности и защиты. Если клиент получает файл конфигурации (.cf) или базу данных с правами на конфигурирование, он получает полный доступ к текстам интерфейса. Стандартных средств шифрования или скрытия отдельных языковых ресурсов в 1С не существует. Следовательно, нам нужно отказаться от хранения переводов в стандартных синонимах и реализовать альтернативный механизм.
Рассмотрим базовый уровень защиты, который не требует глубокого переписывания кода конфигурации, но защищает от большинства рядовых специалистов заказчика.
Однако административные меры бессильны, если у клиента есть квалифицированные специалисты, способные обойти пароли Конфигуратора или восстановить доступ через резервные копии СУБД. Поэтому перейдем к техническим методам защиты.
Чтобы исключить хранение переводов в синонимах конфигурации, мы можем выполнять перевод программно в момент открытия форм. Чтобы не прописывать этот код вручную в сотнях форм (что превратило бы разработку в кошмар), мы воспользуемся механизмом **расширений конфигурации** и возможностями глобального перехвата событий платформы 1С:Предприятие 8.3.
Разберем по шагам, как реализовать этот алгоритм:
ЗащищенныйПереводСервер.ПриСозданииНаСервере для всех форм конфигурации с помощью аннотации &После.Посмотрим на пример реализации такого общего модуля:
&После("ПриСозданииНаСервере")
Процедура Защита_ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Проверяем, включен ли для текущего пользователя иностранный язык
Если ТекущийЯзыкСистемы() = "en" Тогда
// Запускаем рекурсивный обход элементов формы
ПеревестиЭлементыФормы(ЭтотОбъект.Элементы);
// Переводим заголовок самой формы
ЗаголовокФормы = ЭтотОбъект.АвтоЗаголовок ? ЭтотОбъект.Заголовок : ЭтотОбъект.Заголовок;
НовыйЗаголовок = ПолучитьЗащищенныйПеревод(ЗаголовокФормы);
Если ЗначениеЗаполнено(НовыйЗаголовок) Тогда
ЭтотОбъект.Заголовок = НовыйЗаголовок;
ЭтотОбъект.АвтоЗаголовок = Ложь;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Процедура ПеревестиЭлементыФормы(КоллекцияЭлементов) Экспорт
Для Каждого Элемент Из КоллекцияЭлементов Цикл
// 1. Перевод стандартных элементов управления (кнопки, декорации, группы)
Если ТипЗнч(Элемент) = Тип("ДекорацияФормы")
Или ТипЗнч(Элемент) = Тип("КнопкаФормы")
Или ТипЗнч(Элемент) = Тип("ГруппаФормы") Тогда
НовыйТекст = ПолучитьЗащищенныйПеревод(Элемент.Заголовок);
Если ЗначениеЗаполнено(НовыйТекст) Тогда
Элемент.Заголовок = НовыйТекст;
КонецЕсли;
// 2. Перевод полей ввода и полей флажков
ИначеЕсли ТипЗнч(Элемент) = Тип("ПолеФормы") Тогда
НовыйТекст = ПолучитьЗащищенныйПеревод(Элемент.Заголовок);
Если ЗначениеЗаполнено(НовыйТекст) Тогда
Элемент.Заголовок = НовыйТекст;
КонецЕсли;
// Если у поля есть текст всплывающей подсказки
НоваяПодсказка = ПолучитьЗащищенныйПеревод(Элемент.Подсказка);
Если ЗначениеЗаполнено(НоваяПодсказка) Тогда
Элемент.Подсказка = НоваяПодсказка;
КонецЕсли;
// 3. Обработка таблиц и динамических списков (рекурсивный обход колонок)
ИначеЕсли ТипЗнч(Элемент) = Тип("ТаблицаФормы") Тогда
ПеревестиЭлементыФормы(Элемент.ПодчиненныеЭлементы);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Функция ПолучитьЗащищенныйПеревод(Значение) обращается к нашему скрытому словарю. Теперь перед нами стоит ключевой вопрос: как защитить сам этот словарь и код этой функции от декомпиляции и чтения?
Если мы просто положим переводы в макет расширения или напишем их в виде соответствия (Соответствие) прямо в коде, злоумышленник легко вскроет модуль расширения (даже защищенный паролем 1С) с помощью сторонних утилит декомпиляции байт-кода.
Рассмотрим более надежные варианты хранения и защиты данных словаря:
Мы пишем алгоритмы словаря в общем модуле 1С, но перед сборкой поставки обрабатываем этот модуль специализированным **обфускатором байт-кода 1С**. Обфускатор изменяет структуру инструкций виртуальной машины 1С, делает невозможным обратное восстановление исходного кода декомпиляторами. При попытке прочитать такой модуль злоумышленник увидит нечитаемый набор символов или аварийно завершит работу декомпилятора.
Это один из самых надежных «офлайн» методов защиты. Мы выносим словарь переводов во внешнюю скомпилированную библиотеку (.dll для Windows, .so для Linux), написанную на C++ или Rust.
1С обращается к этой компоненте по технологии **Native API**, передавая исходный текст на русском языке, а библиотека возвращает зашифрованный перевод. Взломать скомпилированный бинарный файл C++ на порядок сложнее, чем код 1С.
Самый прогрессивный метод, превращающий перевод в услугу по подписке. Словарь переводов полностью хранится на нашем удаленном веб-сервере. При запуске программы или при открытии каждой конкретной формы конфигурация отправляет защищенный HTTPS-запрос к нашему API, передавая токен лицензии и список строк, требующих перевода.
Схематично этот процесс выглядит так:
Документ.РеализацияТоваровУслуг.https://api.ourtranslation.com/translate.Рассмотрим проблему производительности. Если мы будем выполнять рекурсивный обход элементов формы и делать запросы к СУБД или внешнему серверу при каждом открытии любой формы, пользователи столкнутся со значительными задержками («тормозами») интерфейса.
Чтобы избежать падения производительности, нам необходимо внедрить **кэширование переводов в памяти сеанса**.
Проанализируем пример оптимизированной функции получения перевода с использованием параметров сеанса:
Функция ПолучитьЗащищенныйПеревод(ИсходныйТекст) Экспорт
Если ПустаяСтрока(ИсходныйТекст) Тогда
Возврат ИсходныйТекст;
КонецЕсли;
// Пытаемся получить кэш переводов из параметров сеанса, чтобы не дергать СУБД/API постоянно
КэшПереводов = Неопределено;
Попытка
КэшПереводов = ПараметрыСеанса.КэшЛокализации.Получить();
Исключение
// Параметр сеанса не инициализирован
КонецПопытки;
Если КэшПереводов = Неопределено Тогда
КэшПереводов = Новый Соответствие;
КонецЕсли;
// Ищем перевод в оперативной памяти сеанса
ПереведенныйТекст = КэшПереводов.Получить(ИсходныйТекст);
Если ПереведенныйТекст = Неопределено Тогда
// Если в кэше нет, получаем из защищенного источника (база данных, API или DLL)
ПереведенныйТекст = ПолучитьИзВнешнегоИсточника(ИсходныйТекст);
Если ЗначениеЗаполнено(ПереведенныйТекст) Тогда
// Записываем в кэш для последующих вызовов
КэшПереводов.Вставить(ИсходныйТекст, ПереведенныйТекст);
ПараметрыСеанса.КэшЛокализации = Новый ХранилищеЗначения(КэшПереводов);
КонецЕсли;
КонецЕсли;
Возврат ?(ЗначениеЗаполнено(ПереведенныйТекст), ПереведенныйТекст, ИсходныйТекст);
КонецФункции
Этот подход гарантирует, что обращение к медленным ресурсам (файлам, базам данных или веб-сервисам) произойдет только один раз для каждого уникального термина. Все последующие открытия форм будут отрабатывать мгновенно из оперативной памяти.
Динамический перевод элементов формы отлично решает задачу скрытия интерфейса, но разработчик неизбежно столкнется со сложными объектами, такими как **печатные формы (макеты табличных документов)** и **разделы справки**.
Их защиту следует организовывать следующим образом:
Параметр_ИмяКолонки). Перед выводом табличного документа на печать или экран, в общем модуле печати происходит заполнение этих параметров переведенными значениями из вашей защищенной системы кэша.Надежной «коробочной» защиты перевода внутри 1С не существует, но грамотное использование платформенных механизмов позволяет создать непреодолимый барьер для злоумышленников. Сочетание **динамической подмены интерфейса через расширения**, **кэширования в параметрах сеанса** и **обфускации алгоритмов расшифровки** (или выноса их в облачное API) гарантирует максимальную безопасность ваших инвестиций в локализацию программных продуктов.