Поскольку утечки данных случаются в наши дни все чаще, крайне важно делать приложения максимально защищенными. Одной из основных областей, где можно найти бреши в защите, является файл web.config. Получив доступ к этому файлу, который обычно хранится в виде незашифрованного текста, злоумышленник затем может легко получить доступ к базам данных и множеству других ресурсов, как внутренних, так и внешних.

Так было не всегда. Когда .NET был еще совсем молод, большинство приложений работали в рамках одного домена Windows. Обычно в файле web.config не нужно было хранить пароли; права доступа к базе данных предоставлялись непосредственно учетной записи пользователя, под которой работало приложение.

Для нынешних приложений это больше не вариант. В современном мире распределенных систем базы данных и другие ресурсы часто работают за пределами домена Windows и даже могут контролироваться разными компаниями. В таких условиях правильное хранение паролей, токенов приложений и других идентификаторов системного уровня имеет очень важное значение.

Представленная здесь техника не нова; Йоуни Хейкниеми (Jouni Heikniemi) рассказывал о ней еще в 2013 году, как и команда SQL Azure за три года до этого. Но с годами некоторые шаги перестали работать из-за устаревших инструментов и изменения пользовательских интерфейсов. Тем не менее, первоначальная концепция по-прежнему актуальна.

Дополнительная защита

Прежде чем мы начнем разговор о конкретных методах, важно понять, что шифрование файла web.config — это только один шаг в комплексном процессе обеспечения безопасности приложения. Важно очень четко понимать, что оно защищает, а что нет.

Чтобы от файла web.config была хоть какая-нибудь польза, приложение должно уметь его расшифровывать. Если вы будете хранить ключ дешифрования в файле неподалеку от файла web.config, очевидно, что злоумышленник просто получит доступ к этому в дополнение к файлу web.config. Встраивание ключа дешифрования в приложение немного усложняет работу злоумышленника, но с этим можно справиться, просто запустив декомпилятор на украденном коде вашего приложения.

Цифровые сертификаты бывают разные. Они хранятся на уровне ОС, поэтому злоумышленники не могут просто взять и выудить их с жесткого диска. Если вы явно не разрешите это, их нельзя даже экспортировать с компьютера, на котором они находятся.

Нельзя сказать, что это идеальное решение. Если злоумышленник может производить запись в каталог приложения, приложение может быть изменено для расшифровки и раскрытия содержимого web.config. Это означает, что вы должны заботиться о защите каталогов, содержащих код приложения, от записи. В идеале только ваш сервер сборки может производить запись в ваши каталоги приложений на рабочем сервере, но это уже выходит за рамки этой статьи.

Другой проблемой является защита копий самого цифрового сертификата. Они нужны для настройки новых серверов, но во всех остальных случаях их следует хранить в какой-нибудь оффлайн-хранилище или другом безопасном месте. С другой стороны, вы все-таки можете делиться цифровыми сертификатами, но только в этом есть крайняя необходимость.

Создание цифрового сертификата

Цифровые сертификаты состоят из публичного и приватного ключа. Они аналогичны сертификатам SSL/TLS, но являются самоподписанными, поскольку им не нужно удостоверять, кто создал зашифрованный файл. Раньше Windows-разработчики использовали makecert.exe, но этот инструмент уже устарел. (Его все еще можно найти в Windows SDK.)

В наши дни правильным инструментом для создания сертификатов на компьютерах с Windows считается PowerShell. Чтобы создать сертификат, используйте следующие команды: 

ПРЕДУПРЕЖДЕНИЕ: Возможно, вам придется запускать PowerShell-команды от имени администратора.

$cert = New-SelfSignedCertificate -Type DocumentEncryptionCert -Subject "CN=DevConfig" -KeyExportPolicy Exportable -KeySpec KeyExchange

Export-Certificate -Cert $cert -FilePath ".\DevConfig.cer"

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText

Export-PfxCertificate -Cert $cert -FilePath ".\DevConfig.pfx" -Password $mypwd

$cert

Как вы могли догадаться сами из ее названия, команда New-SelfSignedCertificate создает сертификат. В этом примере он называется “DevConfig”, но вы можете назвать его как хотите.

Затем мы экспортируем сертификат шифрования в “.cer”-файл с помощью команды Export-Certificate. Это публичный ключ, используемый для создания зашифрованных файлов конфигурации.

Следующие две строки позволяют нам экспортировать сертификат расшифровки в виде “.pfx”-файла с помощью Export-PfxCertificate. Крайне важно, чтобы этот файл хранился в безопасном оффлайновом месте отдельно от пароля, который вы определили с помощью ConvertTo-SecureString. (И, конечно, вам следует заменить “1234” более безопасным паролем.)

Для последующих шаговам вам потребуется узнать thumbprint сертификата. Последняя строка, “$cert”, сама по себе будет отображать thumbprint на экране. Thumbprint больше не считается секретом.

Импорт сертификата шифрования в Windows

Если вы настраиваете машину, которая может шифровать, но не расшифровывать файлы конфигурации, тогда вам будет полезен этот шаг. В противном случае перейдите сразу к следующему.

Import-Certificate -Filepath ".\DevConfig.cer" -CertStoreLocation cert:\LocalMachine\My

В разделе Import-Certificate вы найдете больше информации.

Импорт сертификата расшифровки в Windows

На этом шаге будет фактически импортирован и сертификат расшифровки, и сертификат шифрования (также известные как публичный и приватный ключи). 

$mypwd = ConvertTo-SecureString -String "1234" -Force -AsPlainText
Import-PfxCertificate -FilePath ".\DevConfig.pfx" -CertStoreLocation Cert:\LocalMachine\My -Password $mypwd

Если вы импортируете сертификат с помощью Import-PfxCertificate, то по умолчанию он не экспортируется. Это означает, что кто-либо другой не сможет экспортировать его с этой машины на другую. Хотя для продакшена так делать в принципе желательно, вы можете пометить его как экспортируемый для машин разработчиков.

Вы можете использовать средство управления сертификатами, чтобы убедиться, что сертификат был правильно установлен.

Импорт сертификата расшифровки в Windows Azure 

Сертификат расшифровки недоступна в бесплатной версии Azure App Services. На момент написания этой статьи вам нужен как минимум уровень B1, который разрешает SSL-подключения.

На портале Azure вам нужно найти вкладку “SSL Settings”. Затем нажмите кнопку “Upload Certificate” , чтобы указать сертификат. Затем вы должны увидеть его в нижней части экра:

Далее вам нужно предоставить приложению ваши сертификаты. Это делается путем добавления ключа WEBSITE_LOAD_CERTIFICATES на вкладку “Application Settings”. Вы можете использовать несколько значений thumbprint’ов, разделенных запятыми, или можете установить это значение на “*”, чтобы предоставить все ваши сертификаты вашему веб-приложению.

Защищенный поставщик конфигурации

Шифрование и дешифрование обрабатывается ProtectedConfigurationProvider. Тот, который  мы используем в этой статье, называется Pkcs12ProtectedConfigurationProvider. Первоначально он был создан корпорацией Microsoft, но в него были внесены незначительные изменения для совместимости со службами приложений Azure.

ProtectedConfigurationProvider вставляет себя в конвейер чтения конфигурации, поэтому код вашего приложения на самом деле не должен знать об этом. Это полезно, когда вам нужна гибкость использования как зашифрованных, так и незашифрованных файлов конфигурации. 

Вы можете добавить Pkcs12ProtectedConfigurationProvider непосредственно в свой проект или загрузить его с помощью NuGet-пакета WebConfigEncrypter. В этой статье предполагается, что вы будете использовать NuGet-пакет.

Подготовка файла web.config к шифрованию

После добавления в проект класса Pkcs12ProtectedConfigurationProvider вам потребуется подготовить файл web.config для шифрования.   

1. Добавьте этот раздел в свой файл web.config.

<configuration>
  [...]
  <configProtectedData>
    <providers>
      <add name="Pkcs12Provider" thumbprint="1234123412341234123412341234123412341234" type="WebConfigEncrypter.Pkcs12ProtectedConfigurationProvider, WebConfigEncrypter" storeLocation="LocalMachine"/>
    </providers>
  </configProtectedData>

2. Измените атрибут thumbprint, чтобы он соответствовал вашему сертификату.

3. Если вы не используете NuGet-пакет, обновите атрибут type, чтобы он соответствовал полностью квантийицированному классу и имени сборки DLL-библиотеки вашего ProtectedConfigurationProvider.

Шифрование строк подключения в файле web.config

Прежде чем начать это делать, убедитесь, что у вас есть резервная копия файла web.config.

В командной строке Visual Studio введите “where aspnet_regiis”. Затем скопируйте в эту папку следующие файлы. Это позволит инструментам командной строки aspnet использовать ваш класс ProtectedConfigurationProvider:

  • WebConfigEncrypter.dll 

  • System.Configuration.ConfigurationManager.dll

  • System.Security.Cryptography.Xml.dll

Затем запустите эту команду из той же папки, что и файл web.config:

aspnet_regiis -pef "connectionStrings" "." -prov "Pkcs12Provider"

Ваш раздел зашифрованной конфигурации должен выглядеть примерно так:

<connectionStrings configProtectionProvider="Pkcs12Provider">
    <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
      xmlns="http://www.w3.org/2001/04/xmlenc#">
      <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes192-cbc" />
      <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
        <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
          <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
          <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <KeyName>rsaKey</KeyName>
          </KeyInfo>
          <CipherData>
            <CipherValue>Moy/a2XO2zvnn/HZW53DyC8aAJWo16+0KmnpC4SCSmuQZU0RT+HNFEA33pAGCzve+m6MTaRzhx6jVVRoAvpSNzfYG1bU1z7a1YnbW4OGxrmYYfdWW6cZQZ57dZnL6YSAlkJ5WlqPDGUPJa6FV/hTic3x4fJYy5vdSucmO6X3opuo1998LWNkL6fIS4WkjkG/SOFbI2Qx3HHogdN670jDHKNDON1z7bFHhLNyVj7RTO3xuQN9kF4PqbFtvwm1bYXTbZpdNxu/fcXZKONSAu8HN3QX5vTRyP/I4BG+NK7TUig3gxD4tq9GR7aSSGKJyt02PiCEO0JpyyIbHZ9xbck9kw==</CipherValue>
          </CipherData>
        </EncryptedKey>
      </KeyInfo>
      <CipherData>
        <CipherValue>TeV0yJaFlEhpyZUlQoG7M3O7sfQ7uG3ndgmhxipOrwoEsrI+Zvt1NI7arefOFWGNW4CEaoLo4mKy2Kwr4ZgK+6rAwOmx1IRyheWtF7z/8+CiGOqSRXLyGEkDQBEVOWKU0Y6TaWtPu0ZM3bp5pvKaztBnthgGnrGYmigaufu5rZW1GWPtHyL2iWdAkU9iaf+AOpA/GSvoVtZmnfJ1rwy6U8PTO0h0Ws/PdkcOKuXGkx31t/Y32ivFoy7xYPnPt/Z/aNMiHvbO7faQAwuJ/NsG9G1FFRRHCqc73TUsRdKHVuf17BEp526RG6RBZtM3F3V3o0d8/sLmyrNI9tFfksB4qcWiN4P+BRtGr0iacmBfBOvAFSozfUYxjMpx+BYPOpD1pf4fMFoKxxKeJYY31XqZoQLp75RgmWhWYm8URHq4Cjs=</CipherValue>
      </CipherData>
    </EncryptedData>
  </connectionStrings>

Вот и все (для Windows IIS). Ключ configProtectionProvider сообщит вашему приложению, какой класс дешифрования и сертификат использовать. Если это не сработает, повторно запустите описанную выше команду Import-PfxCertificate.

Шифрование пользовательских разделов конфигурации

В дополнение к строкам подключения вы можете зашифровать пользовательские разделы конфигурации, созданные с помощью IConfigurationSectionHandler. Для этого необходимо скопировать библиотеку, определяющую пользовательский класс конфигурации, в ту же папку, что и aspnet_regiis, точно так же, как вы сделали с WebConfigEncrypter.dll.

Убедитесь, что в своем списке configSections вы используете полностью квантифицированный класс и имя сборки для пользовательского класса конфигурации. Например:

 <configSections>
    <section name="protectedSettings" type="MyConfigSectionHandler.MyHandler, WebApplication1" />
  </configSections>

Это необходимо для aspnet_regiis, даже если в противном случае вы можете просто указать имя класса. 

Затем вы можете снова запустить команду шифрования, заменив параметр -pef именем раздела.

aspnet_regiis -pef "protectedSettings" "." -prov "Pkcs12Provider"

Особые рекомендации для Azure App Services

Azure Поскольку службы приложений Azure хранят и предоставляют сертификаты для текущего пользователя, а не для компьютера, вам потребуется изменить атрибут storeLocation, как показано ниже:

  <add name=“Pkcs12Provider” thumbprint=“1234123412341234123412341234123412341234" type=“WebConfigEncrypter.Pkcs12ProtectedConfigurationProvider, WebConfigEncrypter” storeLocation=“CurrentUser”/>

Перевод статьи подготовлен в преддверии старта курса C# ASP.NET Core разработчик. Приглашаем всех на бесплатный урок курса, в рамках которого познакомимся с видами баз данных. Разберем как работать с реляционными и нереляционными базами данных - напрямую и через ORM. Регистрация доступна по ссылке.

Комментарии (0)