Допустим, Вы написали свой собственный программный продукт. Появилось желание этот продукт продать. Для успешной продажи Вашего софта конечно же нужна демонстрация, а лучше всего демонстрационная база данных. Как правило при создании демонстрационной базы данных разработчик каким либо способом ограничивает действие продукта - по времени использования или до определённой даты. К примеру, продукт перестанет работать через 30 запусков или после какой нибудь даты, к примеру 01.06.2018 года.
Первые трудности связаны с практически полностью беззащитным программным кодом, созданным в конфигураторе 1С. И совсем не важно, установлен ли пароль на модули продукта конфигурации или код не включен в поставку. Знающий программист сможет получить написанный Вами код очень легко и всего лишь за каких-нибудь пять минут. Всегда найдутся люди, желающие скачать бесплатную демонстрационную версию базы данных, найти уязвимые места в защите, убрать их и после этого пользоваться продуктом бесплатно и без каких либо ограничений.
Всегда хочется вести разработку красиво и лаконично, без лишних ненужных строк кода и это применимо к коду основной конфигурации. Если же вы разрабатываете демонстрационную версию в основе которой лежит основная конфигурация продукта, то всё же стоит потратить время и сделать код для взломщика невыносимым. Это конечно не панацея, но реально сможет помочь защитить продукт.
Способы защиты демонстрационной конфигурации:
1. Код основной конфигурации можно сделать открытым, код демоверсии всегда делайте закрытым. При закрытом исходном коде отсеются "ленивые" программисты и те, кто не смогут его декомпилировать.
2. Код и состав дерева конфигурации основной конфигурации базы данных всегда должен отличаться от демонстрационной версии - это мера предосторожности для установки обновлений от основной поставки поверх демоверсии. В составе дерева конфигурации демобазы используйте другие имена реквизитов и наименования табличных частей документов и справочников, нежели в составе основной поставки - можно изменить несколько основных реквизитов и все места явного обращения к ним в коде.
3. а) Не храните в явном виде дату окончания действия демоверсии или количество записей.
Пример, создали периодический регистр сведений Служебный с Измерением Дата и Ресурсом Количество запусков.
Далее записали в регистр дату - 01.06.2018 и Количество запусков - 30, - это неправильный вариант. Сделайте измерение дата с типом строка и храните там хешированное значение. Напишите функцию, которая получает запросом из регистра хешированное значение даты и преобразует его в нормальную дату. ниже таблица, как конечный пользователь будет видеть данные такого регистра:
Дата |
Количество запусков |
WоиоЫмGдШf17fД2135135ГПд |
Sdfvhuyj35135465jhjguyu |
То есть функция будет выглядеть примерно так:
Функция Преобразование ()
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| Служебный.Дата КАК Дата,
| Служебный. КоличествоЗапусков КАК КоличествоЗапусков,
|ИЗ
| РегистрСведений. Служебный КАК Служебный";
ТЗ = Запрос.Выполнить().Выгрузить();
Для Каждого Строка из ТЗ Цикл
// здесь будет Ваш код, он будет уникальным, в зависимости от того какой алгоритм придумаете.
// Далее получаем дату и количество запусков программы из строки преобразуем её
КонецЦикла;
Возврат ТЗ; //Возвращает в ТЗ уже значения 01.06.2018 и 30.
КонецФункции
б) Храните дату окончания работы и количество запусков не в одном месте, а в нескольких объектах конфигурации. Например, выше мы храним хэшированные значения в регистре Служебный, сохраняйте такие же значения ещё в каком нибудь справочнике и ещё в какой нибудь константе. При старте системы проверяйте, совпадают ли полученные данные из этих разных мест, если не совпадают, значит продукт взломан.
Есть ещё небольшая хитрость. Вы можете сделать проверку на неверное время, в случае если пользователь переводит системные часы. Проверка может сверять, например, текущий год и значение 2018 год. Если сейчас база запущена и текущая системная дата выставлена 30.09.2017 г, а демоверсия создана в 2018 году, то после проверки вы получите неверное значение.
Ниже пример:
Функция ПроверкаТекущегоГода() //возвращает Ложь если год не 2018
Если Год(ТекущаяДата()) <> Год(Дата("20180601")) Тогда
Возврат Ложь;
Иначе
Возврат Истина;
КонецЕсли;
КонецФункции
в) Храните дату окончания работы и количество запусков в виде бинарного файла, а сам файл поместите в Хранилище значения.
В данном случае можно зашифрованные данные сохранить в виде любой структуры или файла непосредственно в базе и извлекать их специально написанным алгоритмом. Так сейчас в типовых базах хранят, к примеру, картинки и письма и документы.
К этому же способу предлагаю отнести хранение в бинарном виде обработок, если они хранятся в объекте Хранилище значения.
4. Можно хранить любые функции, данные по количеству запусков и дате окончания не в метаданных конфигурации, а в виде DLL, внешнего бинарного файла или параметрами в реестре Windows. Для этого Вам придется работать с компонентами или чтением бинарных файлов. Также можно использовать Flash память или токен в виде лицензионного ключа. Этот пункт не является обязательным.
5. Усложнение кода демонстрационной конфигурации.
а) Будьте трудолюбивы. Не вызывайте одну и ту же функцию из одного места. Разнесите эту функцию, с небольшими изменениями в названии и коде в разные объекты. К примеру, указанную выше функцию Преобразование() вы используете при старте системы вызывая её из общего модуля, затем растиражируйте её в модули различных документов и справочников с названиеми Преобразовыватель(), ПолучениеХэша(), ПросмотрДанных() и т.д. И эти функции вызывайте из модулей форм документов, справочников, регистров.
б) Вызывайте нужную функцию через другие функции несколько раз, передавая результат.
Например,
// Функция ПолучитьЗначение() находится в модуле документа Реализация
Функция ПолучитьЗначение()
Значение = МодульПроверкиСервер.ПолучитьЗначение();
Возврат Значение;
КонецФункции
// Функция ПолучитьЗначениеДляРеализации() находится в общем модуле МодульПроверкиСервер
Функция ПолучитьЗначениеДляРеализации ()
Значение = МодульДополнительнойПроверки.НайтиЗначение();
Возврат Значение;
КонецФункции
Пусть это выглядит бесполезно, зато сильно затруднит работу программиста, пытающегося взломать и понять ваш код.
в) Используйте функции с генератором случайных чисел, вызывая другие "куски" кода в случайном порядке.
Функция ПолучитьЗначениеДляПоступления ()
ГСЧ = Новый ГенераторСлучайныхЧисел();
СлучайноеЧисло = ГСЧ.СлучайноеЧисло(0, 2);
Если СлучайноеЧисло = 0 Тогда
Значение = МодульПроверкиСервер.ПолучитьЗначение();
Возврат Значение;
Иначе Если СлучайноеЧисло = 1 Тогда
Значение = МодульДополнительнойПроверки.НайтиЗначение();
Возврат Значение;
Иначе Если СлучайноеЧисло = 2 Тогда
Значение = МодульВызоваПроверки.ПолучитьЗначениеКонстанты();
Возврат Значение;
Иначе
КонецФункции
6. Использование "служебных" справочников и регистров.
а) Для хранения значений наименований реквизитов форм.
Этот способ позволяет связать метаданные вашей демонстрационной конфигурации с выводом форм пользователю. Например, просто выгрузив cf файл и развернув его в чистую базу, пользователь не сможет прочитать что-то на основных формах документов.
Для примера, возьму документ Реализация товаров и услуг. Пусть у этого документа имеются реквизиты Покупатель, Продавец, Табличная часть с полями - номенклатура, цена, количество. Свойства Заголовок реквизитов формы можно оставить пустым или заполнить символами ХХХХХХХ.
Создадим регистр, в котором будем хранить данные формы - ДанныеПолейФорм, в качестве измерений регистра создадим строковый реквизит ИмяФормы, ресурсы - Наименование, Принадлежность.
Открыв метаданные и заполнив регистр, можно получить примерно такой результат.
ИмяФормы |
Принадлежность |
Наименование |
ОсновнаяФормаРеализации |
Основной |
Продавец |
ФормаСпискаРеализации |
Основной |
Покупатель |
ОсновнаяФормаРеализации |
Основной |
Покупатель |
ОсновнаяФормаРеализации |
ТабличнаяЧасть |
Номенклатура |
Далее ПриОткрытии формы ОсновнаяФормаРеализации запросом обращаемся к регистру, получаем необходимые текстовые поля и подставляем в наименование реквизитов формы. Если же в регистре пусто (как раз пользователь самостоятельно развернул cf в чистую базу) тогда вместо полей на форме пользователь увидит ХХХХХХХ.
В регистре данные можно хранить в виде хэша, так же как и в пункте 3а.
б) Для хранения текстов запросов в справочниках конфигурации.
Создайте служебный справочник с названием ТекстыЗапросов, с строковым реквизитом Текст. Создайте новый элемент справочника и поместите в реквизит Текст текст вашего запроса. Теперь посмотрим, как изменится одна и та же функция, на примере получения наименования номенклатуры.
Функция до изменения:
Функция ПолучитьНаименованиеНоменклатуры()
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура";
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
Функция после изменения:
Функция ПолучитьНаименованиеНоменклатуры()
Запрос = Новый Запрос;
Запрос.Текст = Справочники. ТекстыЗапросов.НайтиПоКоду("00000001");
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
После вышеуказанных действий все тексты запросов будут находиться в метаданных конфигурации.
После заполнения "служебных" справочников или регистров не ленитесь, создайте формы документов и списка в процедуру ПриОткрытии() добавьте строку Отказ = Истина; это для того, чтобы пользователи самостоятельно не заглядывали в эти объекты.
7. Замена переменных и названий функций на не читаемые. Обсфукация кода. Из всех способов замены, больше всего мне нравится делать одинаковые названия процедур и переменных. Посмотрите на слова асAOpHхоВaX и aсAOpHхоВaX - внешне выглядят они совершенно одинаково, но на самом деле это набор из русских и английских букв. В текстовом файле асAOpHхоВaX создал 1024 вариации этого "слова". Далее в коде демонстрационной базы делаем глобальную замену для названий функций и процедур, а внутри каждого модуля можем сделать обычную замену переменных. Продемонстрирую как будет выглядеть код.
Функция до изменения:
Функция ПолучитьЗначениеДляПоступления ()
ГСЧ = Новый ГенераторСлучайныхЧисел();
СлучайноеЧисло = ГСЧ.СлучайноеЧисло(0, 2);
Если СлучайноеЧисло = 0 Тогда
Значение = МодульПроверкиСервер.ПолучитьЗначение();
Возврат Значение;
ИначеЕсли СлучайноеЧисло = 1 Тогда
Значение = МодульДополнительнойПроверки.НайтиЗначение();
Возврат Значение;
ИначеЕсли СлучайноеЧисло = 2 Тогда
Значение = МодульВызоваПроверки.ПолучитьЗначениеКонстанты();
Возврат Значение;
Иначе
КонецФункции
Функция после изменения:
Функция аcАОрНxоВaX()
acАОрНxоВaX = Новый ГенераторСлучайныхЧисел();
асAOpHхoВaX = aсAOpHхoВaX.СлучайноеЧисло(0, 2);
Если асAOpHхoВaX = 0 Тогда
аcAOpHхoВaX = МодульПроверкиСервер. acAOpHхoВaX ();
Возврат аcAOpHхoВaX;
ИначеЕсли асAOpHхoВaX = 1 Тогда
аcAOpHхoВaX = МодульДополнительнойПроверки. асАOpHхoВaX ();
Возврат аcAOpHхoВaX;
ИначеЕсли асAOpHхoВaX = 2 Тогда
аcAOpHхoВaX = МодульВызоваПроверки. aсАOpHхoВaX ();
Возврат аcAOpHхoВaX;
Иначе
КонецФункции
Для того, чтобы "споткнулся" декомпилятор кода, можем убрать символы переноса и лишние пробелы в коде функции, получим такой вот результат:
Функция аcАОрНxоВaX() acАОрНxоВaX = Новый ГенераторСлучайныхЧисел(); асAOpHхoВaX = aсAOpHхoВaX.СлучайноеЧисло(0, 2); Если сAOpHхoВaX = 0 Тогда аcAOpHхoВaX = МодульПроверкиСервер.acAOpHхoВaX (); Возврат аcAOpHхoВaX; ИначеЕсли асAOpHхoВaX = 1 Тогда аcAOpHхoВaX = МодульДополнительнойПроверки. асАOpHхoВaX (); Возврат аcAOpHхoВaX; ИначеЕсли асAOpHхoВaX = 2 Тогда аcAOpHхoВaX = МодульВызоваПроверки. aсАOpHхoВaX (); Возврат аcAOpHхoВaX; Иначе КонецФункции
На работоспособность функции это никак не повлияет, зато представьте, что будет чувствовать программист, "ломающий" ваш код и отслеживающий в конфигураторе значение похожих друг на друга переменных.
Если уже и это не поможет, то поищите обработку, непосредственно смешивающую байт код.
8. Защита компьютеров предприятия на физическом уровне, без возможности копирования базы данных и документов пользователями базы.
Этот пункт подразумевает, что на вашем предприятии усиленная система безопасности и охрана, т.е. полностью запрещён доступ интернет и копирование на внешние накопители, каждый пользователь проверяется. Такая система нередко используется, например в банковской отрасли. Плюсы очевидны - можно не париться с защитой кода, минусы тоже - рано или поздно по халатности какого нибудь сотрудников по обеспечению безопасности база будет скопирована... Однако как в симбиозе с другими методами защиты, этот пункт тоже может быть полезен.
Следующий, 9 пункт не заслуженно пропущенный мной и активно обсуждаемый в комментариях, считаю необходимым добавить. Это, пожалуй, самый надежный из всех способов защиты используемый при наличии у пользователя стабильного доступа в сеть. Добавляю именно из-за того, что этот пункт активно обсуждается участниками сообщества Инфостарт. Это заслуга не моя, а именно тех людей, которые прокомментировали эту статью. Огромное Вам Всем СПАСИБО!
9. Использование демонстрационной версии без возможности использования конфигуратора на стороннем сервере:
а) Использование непосредственно на сервере вашего предприятия, доступ через локальную сеть.
б) Использование облачного сервиса. База полностью или частично (имеется ввиду, частичный вынос кода) хранится в облаке.
Используя такие простые методы в разработке демобазы, вы сможете её защитить, конечно, взломать можно всё что угодно, но оплаченных человеко-часов на это у хакера уйдет гораздо больше, чем стоит ваш продукт. При использовании внешних компонент, систем криптографии и внешних файлов вы сможете защитить свой продукт на 100 процентов.
На этом все. Если статья Вам понравилась и пригодилась, не забудьте оценить, нажимайте на звёздочку.