понедельник, апреля 09, 2007

Безопасное подключение к SQL серверу

Практически любое ASP.NET приложение использует БД и в большинстве случаев это SQL Server. Сегодня я хочу поговорить о способах подключения web приложений к SQL серверу. Тема кажется мне интересной, поскольку существует множество нюансов и тонкостей, оказывающих влияния на этот вопрос. Большинство материалов есть в MSDN, но они разбросаны по разным местам, а некоторые аспекты проблемы освещены в документации и вовсе плохо.
Разрабатывая приложение на ASP.NET, которое использует БД мы неизбежно получаем много уровневое приложение, в глубоком бэкэнде которого лежит SQL сервер с базой данных. Он может лежать на одной машине с нашим приложением, а может и на удаленной. С нашим приложением его связывает тонкая нить в виде строки подключения. И от этой строки зависит очень многое. Надеюсь, в вашем приложении эта строка не зашита в коде многочисленных web форм, а располагается в более пристойном месте вроде web.config. Суровая правда жизни состоит в том, что во время разработки в большинстве случаев строка подключения выглядит примерно так:


"User ID=sa;Password=;Initial Catalog=myDatabase;Data Source=(local);"


В этом нет ничего страшного, если эта строка не перекочует в неизменном виде на «боевой» сервер.
Среди множества параметров строки подключения в контексте нашего обсуждения, нам интересны будут те, что отвечают за безопасность соединения. Здесь существуют два варианта, обычный:-


«Data Source=myServerAddress;Initial Catalog=myDataBase;User Id=myUsername;Password=myPassword;»


и так называемый Trusted Connection (я не берусь переводить этот термин, чтобы никого не запутать)


«Data Source=myServerAddress;Initial Catalog=myDataBase;Integrated Security=SSPI;» или другой вариант
«Server=myServerAddress;Database=myDataBase;Trusted_Connection=True;»

При обычном соединении, мы указывает имя пользователя и пароль в строке подключения (именно это обстоятельство и вызывает массу проблем, о чем мы поговорим позже). Тут могут использоваться имена локальных Windows пользователей (локальные учетные записи), имена пользователей домена AD, или иена пользователей SQL сервера, при условии когда на SQL сервере включены оба типа аутентификации: Windows и SQL. В случае Trusted Connection может использоваться только режим Windows аутентификации SQL сервера. При этом имя пользователя и пароль не указываются в строке подключения. Вместо этого данные о пользователе берутся из текущего контекста безопасности приложения. Это обстоятельство также может вызывать массу проблем именно из-за специфики ASP.NET и мы об этом поговорим.
Когда использовать обычное соединение, а когда Trusted Connection? По мере возможности следует отдавать преимущество использованию Trusted Connection. Обычное соединение следует использовать лишь при невозможности установить Trusted Connection, например, если SQL сервер установлен в другой сети, или при хостинге приложения, когда у вас нет доступа к учетным записям Windows, либо когда вы по каким-то причинам вынуждены использовать режим имперсонации пользователя.
Итак, допустим, мы вынуждены использовать обычное соединение и указывать имя пользователя и пароль в строке подключения. Чем может быть вызвано такое решение? Первое, сервер БД расположен в другой сети и в другом домене, который не связан трастовыми отношениями с нашим доменом. Второй случай – это хостинг, когда мы лишены возможности использовать учетные записи Windows. Третий случай, когда по каким либо причинам в нашем приложении включен пользовательский режим имперсонации ASP.NET. Такой режим описывается в файле web.config настройкой следующего вида:


<identity impersonate=”true”>

В этом случае использование Trusted Connection приведет к тому, что каждое соединение с SQL сервером будет устанавливаться от имени пользователя, инициировавшего web запрос. (Подробнее об имперсонации я писал здесь и здесь) Такой вариант имеет право на существование, но используется очень редко. Если вы хотите избежать этого, единственный вариант - указать имя и пароль в строке подключения SQL.
Любой аудит безопасности с радостью ткнет нас мордой в такую строку подключения, и будет в принципе прав. Отчасти поэтому во Framework 2.0 появилась конфигурационная секция . В принципе мы прекрасно хранили строки подключения как ключи в секции , но Connectionstrings предоставляет нам две важные возможности. Первая состоит в том, что теперь мы можем декларативно ограничить использование тех или иных параметров в строках подключения. Вот например как запретить указывать в строке подключения имя пользователя и пароль:


<connectionStrings>
<add name="DatabaseConnection"
connectionString="Data Source=(local);Initial
Catalog=Northwind;Integrated Security=SSPI;"
KeyRestrictions="User Id=;Password=;Persist Security Info=;"
KeyRestrictionBehavior="PreventUsage" />
</connectionStrings>


Вторая возможность, это то, чего нам так не хватало во Framework 1.1 – возможность шифровать строку подключения. Шифровать ее можно именно в секции connectionstrings при помощи утилиты aspnet_regiis.exe:


aspnet_regiis -pe "connectionStrings" -app "/SampleApplication"

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


<identity impersonate=”true” />

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


<identity impersonate=”true” username=”Domain\Account” password=”password” />


все подключения к SQL будет от имени Domain\Account (впрочем здесь может быть и локальная учетная запись).
Когда имперсонация выключена, SQL Trusted connection будут происходить от имени учетной записи, под которой исполняется рабочий процесс. Рабочий процесс ASP.net это aspnet_wp.exe для IIS 5.1 и w3wp.exe для IIS 6.0. И тут есть масса вариантов, это могут быть втроенные (Built-in), локальные и доменные учетные записи. Кроме того для разных версий IIS они настраиваются по разному. В любом случае, вы можете узнать под какой записью исполняется рабочий процесс IIS, просто откройте Windows Task Manager и найдите в списке процессов aspnet_wp.exe или w3wp.exe. В столбце “User name” вы увидите имя учетной записи процесса.
Давайте сначала рассмотрим IIS 5.1. Для него настройка переметров рабочего процесса ASP.NET осуществляется в файле mashine.config, секция . Заглянув в эту секцию вы увидите массу интересных вещей, но сейчас нас интересуют только параметры userName и password. Именно они определяют имя учетной записи для рабочего процесса aspnet_wp.exe. Для параметра userName предусмотрено два зарезервированных значения. Значение «Machine» соответствует локальной учетной записи ASPNET и является значением по умолчанию. Значение “System” соответствует одноименной учетной записи. Для этих двух значений в параметре password должно быть “AutoGenerate”. Помимо специальных значений вы можете задать здесь любую локальную или доменную учетную запись. Защитить от не скромных глаз пароль и имя учетной записи можно при помощи утилиты aspnet_setreg.exe как это сделать, подробно описано в MSDN. Совет: если ваши ASP.NET приложения взаимодействуют с COM компонентами, используйте учетную запись System либо доменную учетную запись. Это позволит избежать массы проблем.
Для IIS 6.0 все по другому. Вернее не совсем все :). Если вы откроете MMC консоль IIS, встанете на узел “Web Sites, откроете его свойства на вкладке “Service”, то вы найдете там флажок “Run WWW service in IIS 5.0 isolation mode”. Если отметить этот флажок, то все станет работать и настраиваться, как описано выше для IIS 5.1. В родном режиме IIS 6.0 запускает свой рабочий процесс для каждого Application pool. Соответственно учетная запись настраивается в свойствах пула приложений. В последующем, Application Pool назначается конкретным web приложениям. Изменилась и учетная запись по умолчанию для пула. Теперь это специальная локальная учетная запись NETWORK SERVICE.
Таким образом, мы разобрались с тем, под какой учетной записью будет установлен SQL Trusted connection нашего ASP.NET приложения (кстати проверить так ли это легко, заглянув в Enterprisw Manager в раздел Management – Current activity – Process info). Но для установления соединения нам необходимо завести на SQL сервере соответствующий логин (login), а затем создать в БД пользователя (user) для этого логина и назначить ему нужные роли.
Важно понимать, что когда сервер приложений (IIS) и SQL сервер расположены на разных компьютерах, установить SQL Trusted connection для приложения, работающего под локальной учетной записью не получится. А учетные записи ASPNET, SYSTEM относятся именно к локальным. Т.е. нам надо использовать доменную учетную запись для рабочего процесса приложения. Но настроить пул приложений IIS для работы под доменной учетной записью задача довольно сложная. Здесь описаны проблемы которые могут возникнуть и методы их решения. К счастью, разработчики Microsoft подумали о том как облегчить нашу жизнь и создали для этого специальную учетную запись NETWORK SERVICE. Эта локальная учетная запись обладает замечательной особенностью, ее можно использовать для доступа к ресурсам в локальной сети. При этом доступ к сетевым ресурсам осуществляется не анонимно (как для LOCAL SERVICE), а от имени учетной записи машины (компьютера).
Итак, мы имеем ASP.NET приложение, рабочий процесс которого запущен под учетной записью NETWORK SERVICE и SQL сервер на этой же машине. Для того, чтобы настроить SQL Trusted connection приложения к SQL серверу следует создать на SQL сервере логин NT AUTHORITY\NETWORK SERVICE.
Если же приложение и SQL сервер расположены на разных компьютерах то логин на SQL сервере должен быть NT AUTHORITY\[Computername]$, где [Computername] – это имя компьютера, на котором расположено ASP.NET приложение.

Ссылки по теме:
Working with Connection Strings
How to: Secure Connection Strings When Using Data Source Controls
Code Access Security and ADO.NET
Connectionstrings.com

3 комментария:

Анонимный комментирует...

Может лучше схоронить строку подключения в реестре? И учетке под которой работает приложение дать права на чтение данного ключа?
|add key="ConnString"
value="registry:HKLM\SOFTWARE\MyApp\Project,
ConnectionString" /|
Чем шифровать веб.конфиг?

Sergey Rozovik комментирует...

>Может лучше схоронить строку подключения в реестре?
Это уже кастомный подход. Я же рассматривал только встроенные возможности.

Kosten комментирует...

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