Защита паролей в конфигах с помощью Windows Data Protection API
[csharp
|
java
|
dpapi
|
windows
]
Abstract
В этой небольшой заметке поделюсь опытом защиты паролей в конфигах приложения. Опишу подводные камни, с которыми столкнулся в процессе интеграции Windows Data Protection API (DPAPI) в код наших модулей на C# и Java.
Introduction
Наше приложение, как это в общем-то принято, всякие логины-пароли к сторонним сервисам хранит у себя в конфигах. Хранить пароли в открытом виде это не очень, но какое-то время до этого руки не доходили. А тут, значит, дошли. Пароли надо шифровать.
Единственной целевой платформой (на тот момент) была Windows, поэтому после небольшого исследования мы остановили свой выбор на DPAPI. Код работы с конфигом в целом и шифрование в частности достаточно инкапсулирован, при необходимости работу с шифрованием несложно заменить.
Methods
DPAPI достаточно примитивно и по сути имеет два метода - зашифровать и расшифровать, Protect и Unprotect, соответственно.
Пример кода на C#; библиотека на Java; если вас не устраивает лицензия (LGPL), вся работа с DPAPI умещается в пару экранов, можно написать самому.
Но расшифровка самим приложением на рантайме это полдела. Перед запуском, но после установки и задания паролей, конфиги надо зашифровать. А это самое интересное!
К сожалению, до инсталлятора руки тоже пока не дошли, и установка производится bat-скриптами (не шучу). Конфигов несколько, структура у них сложная, паролей внутри много; шифровать конфиг целиком нельзя. Писать логику парсинга сложного конфига в bat-скрипте — занятие весьма на любителя.
Сделали вот что: в существующий пайплайн bat-скриптов добавляем один новый, который вызывает Powershell-скрипт. А в него можно вставлять логику на C#, а это совсем другое дело. Profit (почти)!
Создаём отдельную сборку на C#, там пишем всю обработку конфигов, шифрование… не забываем тестировать, всё как положено.
Нюансы:
- В Powershell-скриптах можно использовать сборки на C# до 5.0 включительно (никаких тебе inline out-переменных), поэтому код надо писать с оглядкой на это (в студии в настройках проекта можно указывать версию языка).
- C# внутри Powershell’а можно использовать разными способами:
- указывать сборки и использовать классы из них;
- вставлять прямо в скрипт код на C#. При выполнении скрипта он соберётся во временную сборку и загрузится в память.
Плюс второго способ в том, что не нужно отдельно собирать сборку по работе с конфигом, делать зависимые билды на CI и прочее. Минус — дублирование C# кода (в модуле и скрипте). Смотрите сами.
Ещё момент: шифрование должно производиться под тем же пользователем, под которым потом будет работать приложение (иначе не расшифруется), а скрипты могут запускаются под другим.
Значит нужно запросить credentials нужного пользователя, и запустить скрипт шифрования от его имени.
$account = Get-Credential
if (!$account)
{
Write-Host("Authentication failed. Invalid user name and/or password.") -ForegroundColor red
pause
[Environment]::Exit(1)
}
Start-Process powershell -Credential $account -Argument "whoami `r`n pause"
Results
На выходе имеем цепочку: bat-скрипт -> Powershell-скрипт для запроса логина/пароля пользователя и запуска от его имени -> Powershell-скрипт для самого шифрования, где внури лежит код на C#.
Да, немного громоздко, зато каждую часть удобно отлаживать и тестировать отдельно, и парсинг конфигов написан на нормальных языках (C#, Java). Успех!