При разработке рабочих мест и аналитических панелей часто возникает задача ограничить доступ к конфиденциальной информации (например, к закладке с расчетом заработной платы или окладов), даже если пользователь уже авторизован в базе. Стандартная постановка задачи: при попытке открыть закрытую вкладку запросить у пользователя ввод текущего пароля от его учетной записи 1С и открыть раздел только в случае успешной проверки.
Разберем, почему нельзя просто вызвать стандартное системное окно входа 1С посреди сеанса, и подробно рассмотрим доступные технические решения для безопасной проверки пароля.
В типовых конфигурациях на базе Библиотеки стандартных подсистем (БСП) используется механизм получения хэша введенного пароля через временного пользователя информационной базы. Платформа 1С не хранит пароли в открытом виде — в объекте ПользовательИнформационнойБазы доступно только свойство СохраняемоеЗначениеПароля.
Рассмотрим, как работает данный алгоритм на клиенте и сервере. На клиенте мы запрашиваем строку с маской пароля, а на сервере выполняем проверку:
&НаКлиенте
Процедура ПриПереходеНаЗакладкуОклад(Команда)
ПараметрыВвода = Новый ПараметрыДиалогаВводаДанных;
ПараметрыВвода.Подсказка = "Введите пароль вашей учетной записи 1С:";
ПараметрыВвода.Маска = Истина;
Оповещение = Новый ОписаниеОповещения("ПослеВводаПароля", ЭтотОбъект);
ПоказатьВводТекста(Оповещение, "", "Подтверждение доступа", ПараметрыВвода);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВводаПароля(ВведенныйПароль, ДополнительныеПараметры) Экспорт
Если ВведенныйПароль = Неопределено Тогда
// Пользователь отменил ввод
Возврат;
КонецЕсли;
Если ПроверитьПарольТекущегоПользователя(ВведенныйПароль) Тогда
// Пароль верный — открываем страницу
Элементы.ГруппаСтраницы.ТекущаяСтраница = Элементы.СтраницаОклад;
Иначе
ПоказатьПредупреждение(, "Введен неверный пароль. Доступ ограничен.");
КонецЕсли;
КонецПроцедуры
На сервере в привилегированном режиме выполняем создание временного пользователя, считываем сформированный платформой хэш и сразу удаляем временный объект:
&НаСервереБезКонтекста
Функция ПроверитьПарольТекущегоПользователя(ВведенныйПароль)
УстановитьПривилегированныйРежим(Истина);
ТекущийПользовательИБ = ПользователиИнформационнойБазы.ТекущийПользователь();
Если ТекущийПользовательИБ = Неопределено Тогда
Возврат Ложь;
КонецЕсли;
// Создаем временного пользователя для генерации хэша
ВременныйПользовательИБ = ПользователиИнформационнойБазы.СоздатьПользователя();
ВременныйПользовательИБ.АутентификацияСтандартная = Ложь;
ВременныйПользовательИБ.Пароль = ВведенныйПароль;
ВременныйПользовательИБ.Имя = "TempAuthCheck_" + Строка(Новый УникальныйИдентификатор);
ВременныйПользовательИБ.Записать();
// Считываем сформированное платформой сохраняемое значение
ВременныйПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
ВременныйПользовательИБ.УникальныйИдентификатор
);
ХэшВведенногоПароля = ВременныйПользовательИБ.СохраняемоеЗначениеПароля;
// Удаляем временного пользователя
ВременныйПользовательИБ.Удалить();
// Сравниваем со значением текущего пользователя
Возврат (ХэшВведенногоПароля = ТекущийПользовательИБ.СохраняемоеЗначениеПароля);
КонецФункции
Чтобы не создавать и не удалять новых пользователей в служебных таблицах, разберем альтернативный вариант — применение транзакции к объекту текущего пользователя с последующей ее отменой:
СохраняемоеЗначениеПароля текущего пользователя.НачатьТранзакцию().Пароль введенный пользователем текст и вызываем Записать().СохраняемоеЗначениеПароля.ОтменитьТранзакцию(), чтобы изменения пароля не сохранились в базе.
&НаСервереБезКонтекста
Функция ПроверитьПарольЧерезТранзакцию(ВведенныйПароль)
УстановитьПривилегированныйРежим(Истина);
ПользовательИБ = ПользователиИнформационнойБазы.ТекущийПользователь();
Если ПользовательИБ = Неопределено Тогда
Возврат Ложь;
КонецЕсли;
ИсходныйХэш = ПользовательИБ.СохраняемоеЗначениеПароля;
НачатьТранзакцию();
Попытка
ПользовательИБ.Пароль = ВведенныйПароль;
ПользовательИБ.Записать();
// Считываем обновленный объект
ПроверяемыйПользователь = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(ПользовательИБ.УникальныйИдентификатор);
НовыйХэш = ПроверяемыйПользователь.СохраняемоеЗначениеПароля;
ОтменитьТранзакцию();
Возврат (ИсходныйХэш = НовыйХэш);
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
КонецФункции
Платформа 1С формирует СохраняемоеЗначениеПароля по определенному стандарту: строка содержит два хэша SHA-1 в кодировке Base64, разделенных запятой. Первый хэш считается от строки с сохранением регистра, второй — от строки в верхнем регистре (ВРег).
Посмотрим на программную реализацию расчета хэша без записи пользователя:
&НаСервереБезКонтекста
Функция ПолучитьХэшПароля1С(Пароль)
ХешированиеРегистр = Новый ХешированиеДанных(ХешФункция.SHA1);
ХешированиеРегистр.Добавить(Пароль);
Хэш1 = Base64Строка(ХешированиеРегистр.ХешСумма);
ХешированиеВерхний = Новый ХешированиеДанных(ХешФункция.SHA1);
ХешированиеВерхний.Добавить(ВРег(Пароль));
Хэш2 = Base64Строка(ХешированиеВерхний.ХешСумма);
Возврат Хэш1 + "," + Хэш2;
КонецФункции
&НаСервереБезКонтекста
Функция ПроверитьПарольРасчетомХэша(ВведенныйПароль)
УстановитьПривилегированныйРежим(Истина);
ПользовательИБ = ПользователиИнформационнойБазы.ТекущийПользователь();
Если ПользовательИБ = Неопределено Тогда
Возврат Ложь;
КонецЕсли;
ВычисленныйХэш = ПолучитьХэшПароля1С(ВведенныйПароль);
Возврат (ВычисленныйХэш = ПользовательИБ.СохраняемоеЗначениеПароля);
КонецФункции
Если требуется вызвать именно оригинальное системное диалоговое окно блокировки платформы 1С, можно применить метод глобального контекста ЗаблокироватьРаботуПользователя(). Однако у этого подхода есть специфика: окно полностью блокирует весь интерфейс программы до тех пор, пока не будет введен верный пароль. Если пользователь откажется от ввода или ошибется, вернуться к предыдущей закладке не удастся — сеанс останется заблокированным, и единственным выходом будет закрытие программы.
СохраняемоеЗначениеПароля и работа с коллекцией ПользователиИнформационнойБазы требуют полных административных прав. Чтобы обычные менеджеры не получали ошибку нарушения прав доступа, проверку необходимо выполнять в привилегированном модуле или внутри конструкции УстановитьПривилегированныйРежим(Истина).АутентификацияСтандартная = Ложь). В этом случае свойство СохраняемоеЗначениеПароля будет пустым.