Оглавление
2. Как лечить
5. Описание JWT аутентификации в Vault для 1С простыми словами
6. Небольшой экскурс в криптографию
7. Описание JWT аутентификации в Vault для 1С сложными словами
8. План работ
10. Публикация открытого ключа (JWKS)
11. Настройка Vault
12. Подключение к Vault из 1С с помощью демонстрационной конфигурации
13. И самое главное
14. Благодарности
В чем проблема
Существует проблема такая - пароли лежат где попало. В программном коде, в справочниках, в файлах на диске, прилеплены на монитор. И это плохо. С листочком на мониторе, правда, нам побороться не удастся.
Предлагаю сразу же отойти от понятия "пароль" и использовать "секрет". Потому что чувствительная информация может быть не только паролем, но и строкой подключения, сертификатом, логином, токеном, какими-то реквизитами - чем угодно.
Чем опасен такой подход хранения секретов? Тем, что благодаря хранению секретов в базе, угроза может распространяться на другие участки ИТ-ландшафта вашей компании. Если секреты есть в программном коде - они начинают распространяться по репозиториям. Новости последних лет говорят о множестве утечек баз с персональными и платежными данными, адресами, даже кодами домофонов. Сейчас речь не про хитрые взломы, злых хакеров и квантовые компьютеры. В реальной жизни все проще - воруют бэкапы. Далее из этого бэкапа можно достать все секреты и начать исследовать корпоративную флору и фауну на предмет ценных данных. А это означает, что такую уязвимость необходимо устранить. В бэкапах не должно быть секретов.
Кстати, правительство занимается вопросом ужесточения ответственности за утечки данных.
Как лечить
Куда вынести секреты? В отдельную систему, специально разработанную для этого.
Я выбрал для этого Hashicorp Vault.
Кроме того, что в Спортмастере он уже давно используется, есть еще некоторые критерии выбора:
- opensource https://github.com/hashicorp/vault
- бесплатная версия, покрывающая много потребностей
- хранилище секретов (база данных) зашифровано
- множество методов аутентификации
- удобный web-интерфейс управления
- система ролей и прав
- написан на golang, быстро работает
- большое сообщество
- Hashicorp - известный производитель ПО (Terraform, Consul, Nomad, Vagrant...)
- частые обновления
Все в одной корзине?
Первая мысль, возникающая при идее перенести куда-то все секреты из базы, выражается как "Сложить все яйца в одну корзину". В случае с Vault так ли это? Да, именно так. Однако, это надежная, отказоустойчивая, безопасная и быстрая корзина.
Про скорость уже была ссылка выше. Golang известен своим быстродействием.
Надежная и отказоустойчивая - потому что Vault может работать в кластере. То есть Vault можно расположить на нескольких машинах. Одна машина ведущая, остальные - ведомые. Между ними постоянно автоматически происходит синхронизация данных. Таким образом, база данных Vault постоянно реплицируется в нескольких местах. Благодаря такому подходу при выводе из строя одной машины информация не потеряется, можно будет переключиться на использование другой ноды.
Так же с базой данных Vault можно производить резервное копирование и восстановление в автоматическом режиме через API.
Безопасность Vault главным образом осуществляется тем, что Vault при включении требует очень длинный ключ для доступа к своей БД. Процесс называется "запечатывание" и "распечатывание" (seal и unseal). Этот ключ Vault хранит в ОЗУ, а не на диске. При перезагрузке машины или сервиса нужно обязательно ввести этот ключ, чтобы Vault мог взаимодействовать со своей БД. Поэтому если кто-то унесет с собой БД Vault - ничего страшного, ее просто не получится расшифровать.
Один секрет вместо многих
Вторая мысль, возникающая при идее перенести куда-то все секреты из базы, выражается как "У нас будет один секрет от всех наших секретов". В контексте данной статьи это не совсем так, хотя немного похоже. У нашего секрета будут содержащиеся в нем время жизни и цифровая подпись асимметричным ключом.
Предлагаю воспользоваться одним из методов аутентификации в Vault под названием JSON Web Token (JWT).
Одна статья про JWT - https://habr.com/ru/articles/340146/
Вторая - //infostart.ru/1c/tools/2018297/
Третья - //infostart.ru/1c/articles/2049126/
Сайт с отладчиком - https://jwt.io/
Спецификация JWT - https://datatracker.ietf.org/doc/html/rfc7519
Гугл - https://www.google.ru/
Главные плюсы, которые мы возьмем из JWT:
- Его можно подписать с помощью асимметричного ключа. Таким образом, никто за нас не сможет сделать "фейковый" токен. Произвести токен может только наша конкретная ИБ 1С.
- В токен можно заложить время жизни (срок годности). Vault проверяет эту дату еще до проверки цифровой подписи. С помощью этого свойства мы можем не переживать о том, что токен украли. Скорее всего, к моменту утечки токен уже будет не действителен.
Описание JWT аутентификации в Vault для 1С простыми словами
JWT - это JSON, в котором содержится информация о том, кто его предоставляет. Например, внутри может быть имя базы 1С, которая выступает эмитентом этого токена. Эта строка JSON конвертируется в base64 строку, а уже эта base64 строка подписывается цифровой подписью и добавляется в конец токена.
Академический пример JWT с сайта https://jwt.io/:
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
JWT всегда разделен двумя точками на 3 части:
1. Техническая информация токена. В примере это eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
2. Полезная нагрузка (там мы говорим, кто мы есть, плюс задаем срок годности токена). В примере это eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
3. Подпись закрытым ключом. В примере это SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c. Здесь нет никакого понятного представления. Просто цифровая подпись первых двух частей JWT.
Представьте, что вы (1С) звоните своему другу (Vault):
1С (на первой линии): Привет, Vault! Я 1С ERP, дай мне, пожалуйста, значение секрета с именем "ОченьСтрашныйСекрет". Вот моя цифровая подпись, можешь ее проверить, перезвонив мне по телефону, который ты уже давно знаешь.
Vault (на первой линии): Привет, 1С ERP! Хорошо. Я знаю кто ты, вижу, что у тебя есть доступ к этому секрету. Сейчас я возьму твою подпись и перезвоню тебе, проверю, точно ли это ты. Если подпись подтвердится - я дам тебе одноразовый ключ для доступа к секрету.
Vault (на второй линии): 1С ERP, дай, пожалуйста, открытый ключ для проверки твоей подписи.
1С (на второй линии): держи
Vault (на первой линии): да, я проверил твою подпись твоим открытым ключом, верю, что это действительно ты, держи одноразовый ключ доступа.
1С (на первой линии): *получает секрет с одноразовым ключом*
Картинка с внутрикорпоративной презентации:
То есть никакого пароля нет. 1С сама себе производит пару ключей (открытый и закрытый), создает токен с заданным временем жизни (мне показалось достаточно 30 минут), подписывает токен закрытым ключом и сразу же этот закрытый ключ забывает. Токен сохраняется в базе. И в этом нет ничего страшного, так как у него ограниченное время жизни. А открытую часть ключа 1С публикует в HTTP сервис, на который смотрит Vault. Vault принимает токен, проверяет его подпись с помощью открытого ключа, на этом принимает решение об аутентификации и авторизации. Получается, что 1С как бы сама себя аутентифицирует в Vault. А Vault просто знает где взять открытый ключ для конкретной системы, с помощью которого можно проверить подпись токена, подписанную закрытым ключом.
Небольшой экскурс в криптографию
В статье никто не "изобретал криптографию". Вся криптография обрабатывается фреймворком .NET, который уже установлен на каждой Windows машине.
Пользоваться будем RSA.
Ссылка на хорошую статью по RSA
Это алгоритм шифрования, где есть открытый и закрытый ключ. С помощью закрытого ключа сообщение расшифровывается (или подписывается). Закрытый ключ никто не должен знать. Обладая закрытым ключом, можно расшифровать сообщение или притвориться эмитентом подписи. С помощью открытого ключа сообщение можно зашифровать (или проверить подпись). Открытый ключ можно ни от кого не скрывать, с помощью него ничего страшного сделать нельзя.
Польза алгоритма шифрования RSA достигается за счет того, что открытая и закрытая часть ключа - обязательно связаны друг с другом. Это два очень больших числа. Имея подписанное сообщение и открытую часть ключа - очень трудно вычислить закрытую часть ключа. Однако же, имея подписанное сообщение и открытый ключ - очень просто проверить подпись.
Будем пользоваться RSA с длиной ключа 2048 бит. Меньшая длина ключа - deprecated, так как поддается различным математическим атакам на алгоритм RSA с современными вычислительными мощностями. Большую длину ключа можно сделать, но особого смысла нет.
При помощи RSA работает большая часть Интернета. Есть, конечно, всякая экзотика в виде эллиптических кривых (например https://github.com работает на EC). Но, судя по тому, какие споры об этом есть на Хабре, лучше пользоваться старым добрым RSA с доказанной эффективностью.
Описание JWT аутентификации в Vault для 1С сложными словами
Подпись токена будет происходить асимметричным ключом. Симметричные ключи в токенах - deprecated. Сам Vault отказался от симметричного шифрования в токенах в недавнем обновлении. Ключ и подпись в демо конфигурации осуществляется через COM объекты с помощью .NET, поэтому ничего нового на сервер устанавливать не потребуется. У кого Linux - попробуйте вызывать методы openssl. Нужен будет метод генерации ключа в виде XML и метод подписи данных с помощью этого ключа.
В 1С есть объект ТокенДоступа. И до версии 1С 8.3.24 этот ТокенДоступа формировался неправильно. Конкретно - в JSON`е токена числовые поля iat (ussued at, время создания) и exp (expiration time, время жизни) 1С формировала как строки. А это не по спецификации. Библиотека go-jose, которая используется Vault для обработки JWT, валится на таком 1Сном токене. Поэтому в демо конфигурации подпись токена происходит с помощью .NET через COM.
Тикет https://bugboard.v8.1c.ru/error/000145179
Исправлено в 8.3.23.2137, 8.3.24.1467, 8.3.25.1257.
Сервер 1С не позволяет сохранить у себя в памяти процесса некоторое значение, которое мы сможем переиспользовать из нового сеанса, при этом не записав его на диск (костыли с долго живущими HTTP сервисами просьба не предлагать). Поэтому закрытый ключ будет выкидываться сразу после подписи токена. Ключ будет новый каждый раз для каждого токена. Выглядит как расточительство, но что поделать... С другой стороны ничего страшного. Диапазон ключей огромен, формируется новый ключ очень быстро.
Итак, как выглядит демо конфигурация:
- Справочник с настройками Vault. В демо конфигурации он не привязан к строке подключения 1С, но лучше привязать. В нем будет храниться адрес сервера Vault, имя роли (далее), токен доступа (JWT), открытый ключ и время жизни токена.
- HTTP сервис, отображающий открытый ключ из справочника настроек.
- Роль для доступа к HTTP сервису.
- Регламентное задание обновления токена (JWT) по настройкам.
- Общий модуль для обработки всего этого.
План работ
Нужно уметь создавать ключ RSA, формировать JWT токен, подписывать токен закрытым ключом. Подписанный токен и открытый ключ сохранять в базе. И делать это, например, раз в 30 минут. Токен будет действовать 30 минут, через это время он будет протухать, а мы делать новый токен. Авторизовываться в Vault будем с помощью токена JWT. В Vault есть лаг в одну минуту на обновление ключа. Так что фактически токен будет действовать 31 минуту. Открытый ключ будем публиковать в HTTP сервисе. В Vault укажем адрес публикации, чтобы Vault мог проверять подпись JWT токена.
Ну и настроить Vault, само собой.
Технические дебри
Чтобы сформировать JWT, нам нужно получить случайную пару ключей. Получим мы ее так:
Выглядит костыльно. Здесь powershell вызывает конструктор RSACryptoServiceProvider из .NET и возвращает новый ключ как XML с содержанием закрытой части ключа ($true). Зато закрытый ключ на диск не пишем.
Далее нужно сделать первые 2 части JWT
Превращается в такую строку c помощью преобразования в Base64Url строку: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9. Первую часть можно даже захардкодить, всегда будет одинаковая.
Примечание: не путать Base64Url и Base64. Base64Url - это Base64, но все "+" и "/" заменяются на "-" и "_" соответственно, убираются все переносы строк и все "=" в конце. В общем, преобразуется к требованиям URL строки.
Везде "base_name" нужно заменить на имя своей базы. Требование к имени как к переменным в коде.
Что тем же Base64 URL преобразуется в eyJzdWIiOiJiYXNlX25hbWUiLCJiYXNlX25hbWUiOiJiYXNlX25hbWUiLCJraWQiOiJiZjViZTNmNS00NDBkLTQ0ODEtODg5NS1mMTdjOGQ0ZDY3YjEiLCJpYXQiOjE3MTQ3NDM2MTksImV4cCI6MTcxNDc0NTQxOX0
Здесь "sub": "base_name". Поле subject, содержащее имя нашей базы 1С. Vault по этому полю будет определять пользователя.
"base_name": "base_name" - трудно объяснить, проще сказать "таков путь". Внутри Vault есть требование к наличию дополнительного поля в полезной нагрузке токена. Пусть будет такое. Не забываем заменять base_name на имя своей базы.
"kid": "bf5be3f5-440d-4481-8895-f17c8d4d67b1" - уникальный идентификатор открытого ключа, с помощью которого подписан токен. Просто случайный ГУИД, который надо вставить в токен. И который потом будет опубликован в HTTP сервисе с открытым ключом. Vault по этому ГУИДу будет искать открытый ключ к токену.
"iat": 1714743619 - issued at - токен выпущен 3 мая 2024 года в 16:40:19.
"exp": 1714745419 - expired at - токен действителен до 3 мая 2024 года в 17:10:19.
Третья часть JWT - подпись. У нас уже есть первые 2 части и выглядят они так:
Из этой строки необходимо получить SHA256 хеш-сумму (1С умеет ее делать) и полученную сумму подписать с помощью Cryptography.RSACryptoServiceProvider в .NET.
Функция подписи (да и формирования токена) выглядит так:
Публикация открытого ключа (JWKS)
JWKS - JSON Web Key Set - набор ключей JSON Web
Формируется в функции "НаборКлючей" в списке функций чуть выше. Этот ключ должен быть опубликован в http-сервисе.
Строку подключения к http-сервису необходимо сформировать с inline-авторизацией.
Шаблон:
https://<имя пользователя>:<пароль>@<имя сервера>/<имя базы>/hs/<имя публикации>
Пример:
https://vault_user:w3f21f2e4f234@my1cserver.local/base_name/hs/jwks
Само собой, роль для такого пользователя должна иметь права исключительно на чтение открытого ключа (точнее набора ключей JWKS).
Настройка Vault
Для начала его надо установить. Скачать для своей платформы можно с официального сайта: https://developer.hashicorp.com/vault/install?product_intent=vault
Если сайт не открывается или не стартует загрузка, то, образно говоря, кхм, попробуйте открыть и загрузить из другой локации.
С чего можно начать настройку: https://developer.hashicorp.com/vault/docs/install
Полностью установку расписывать не буду. Лишь уверю в том, что любой более-менее опытный администратор с этим справится. Я устанавливал на Ubuntu и на Windows Server. Не забудьте открыть порт и сохранить ключи для распечатывания (unseal) в надежное место (но не в сам Vault). Ключи для распечатывания необходимы для подключения Vault к файлу своей базы данных при перезапуске.
Когда настроили сервер Vault, настало время произвести настройку JWT авторизации для нашей 1С базы.
Сначала необходимо придумать смысловое имя для базы. Например, "erp_prod" или "buha_intg". Неизвестно, куда база 1С может переехать, а перенастраивать Vault лишний раз не хочется. Хотя и придется, так как публикация JWKS скорее всего переедет вместе с базой.
Далее будет использоваться имя базы test_base_name.
Метод аутентификации создан. Теперь необходимо создать KV-хранилище (key - value, ключ - значение), где и будут храниться наши секреты :)
Осталось немного, донастроить политику доступа для нашей системы:
И создать роль:
Vault для одной базы 1С настроен. Можно создать секрет и попытаться получить его из 1С.
Подключение к Vault из 1С с помощью демонстрационной конфигурации
Исходники демо конфигурации: https://github.com/infina15/1c_vault_demo
Сам CF: https://github.com/infina15/1c_vault_demo/releases/download/1.0.0/vault_demo.cf
Внутри использована консоль регламентных заданий https://github.com/kuzyara/JobsConsole2019.epf. Kuzyara, выражаю большую благодарность!
В справочнике "Vault настройка" у предопределенного элемента Vault установить адрес сервера Vault и порт (если порт не стандартный), имя роли (имя системы, для примера использовалось "test_base_name"), время жизни токена в минутах (по-умолчанию робот проставит 30 минут).
Важно: время жизни токена должно совпадать с регламентным заданием обновления ключа. По-умолчанию время жизни и время обновления составляет 30 минут.
Выполнить регламентное задание Волт_ОбновитьКлюч (в демо конфигурации есть обработка Консоль заданий 2019).
После выполнения рег. задания в справочнике "Vault настройка" должны заполниться поля ТокенДоступа и Открытый ключ.
Далее необходимо создать пользователя с необходимыми правами для обращения к HTTP-сервису Волт_ОткрытыйКлюч. Пример прав - роль Волт_ЧтениеКлюча.
Выполнить публикацию HTTP-сервиса Волт_ОткрытыйКлюч. Сформировать строку подключения в виде http(s)://<имя пользователя>:<пароль>@<имясервера>/<имя базы>/hs/jwks , где <имя пользователя> и <пароль> - имя пользователя и его пароль в ИБ 1С, а <имя сервера> - имя сервера, где произведена публикация HTTP-сервиса 1С с открытым ключом. При формировании имени пользователя и пароля необходимо учесть, что в URL-строке допустыми не все символы. Справка по символам - https://developers.google.com/maps/url-encoding?hl=ru#special-characters
В браузере будет выглядеть так:
Убедиться, что сформированная строка открывается в браузере, открытый ключ в виде json отображается (поля kty, kid, e, n, use, alg). JSON должен совпадать с полем "Открытый ключ" справочника "Vault настройка".
Произвести настройку Vault, используя имя роли (имя базы) из инструкции сверху и сформированный в предыдущем пункте адрес открытого ключа.
С помощью обработки "Волт тест" проверить работу подключения. Например, для пространства Vault с именем test, для его подпапки ab, для лежащего внутри секрета cd, строка пути будет /v1/test/data/ab. В поле Имя секрета нужно указать cd.
Для получения секрета в коде конфигурации необходимо использовать функцию ПолучитьСекрет(Путь, ИмяСекрета) общего модуля РаботаСВолт. По примеру из предыдущего пункта "РаботаСВолт.ПолучитьСекрет("/v1/test/data/ab", "cd")"
И самое главное
После переноса секретов в Vault - обязательно сменить пароли в секретах. Желательно очистить историю данных в тех структурах, где ранее хранились секреты.
Благодарности
Спасибо за прочтение статьи! Буду рад обсудить в комментариях.
Отдельное большое спасибо uno-c за комментарии к статье 001. Криптография и цифровая подпись RSA-sha256 на платформе 1С , без этой информации я вряд ли справился бы с этой задачей.