Main page www.securesize.com  
Main page GeSWall BOWall Resources  
Resources
  Безопасность SQL приложений
Безопасность SQL приложений

Andrey Kolishchak

Ноябрь, 2000


Безопасность SQL приложений


На сегодняшний день большинство приложений оперирует большими массивами данных посредством использования SQL технологий. Помимо обычных прикладных задач это касается и задач системного администрирования: анализ статистики, журналов событий и прочее. Разработка web приложений также уже не мыслима без базы данных на основе SQL сервера. Однако, использование столь мощного инструментария связано с определенным риском утечки, изменения или уничтожения хранимых данных. В данной статье описаны основные уязвимости встречающиеся в SQL приложениях на базе SQL серверов Oracle, MS SQL, Interbase и способы их предотвращения.

Рассмотрим ряд типичных примеров уязвимостей.
В различных приложениях часто можно встретить выполнения следующей команды:

      SELECT * FROM USER_INFO
      USER_NAME = $user,

Результатом которой будет некоторая информация о пользователе с именем $user. Типичный SQL запрос, не правда ли ? Однако, если параметр $user задается пользователем и является не типизированным параметром, а макросом-подстановкой, то такой запрос несет в себе массу опасностей. Опасности начинаются, когда вместо корректного имени пользователь введет часть SQL запроса. Например, пользователь вводит следующее имя:

       'VASYA'; SELECT * FROM USER_INFO
или 'VASYA'; DROP FROM USER_INFO

В результате подстановки параметра $user получится запрос:

SELECT * FROM USER_INFO
WHERE USER_NAME =
'VASYA'; SELECT * FROM USER_INFO

или

SELECT * FROM USER_INFO
WHERE USER_NAME =
'VASYA'; DROP FROM USER_INFO

Таким образом, SQL сервер, поддерживающий пакетные запросы (MSSQL, Oracle) помимо заданного запроса выполнит еще выборку содержимого всей таблицы USER_INFO или удаление всего содержимого этой таблицы.

Рассмотрим другой пример, когда производится авторизация пользователя для некоторой системы:

       SELECT USER_ID FROM ACCOUNT
       WHERE (LOGIN = $login) AND (PASSWORD = $password),

где выбирается идентификатор пользователя с заданным именем и паролем. Идентификатор затем используется для определения прав на выполнение различных действий. В данном случае злоумышленник не имея представления о пароле, но зная имя входа некоторого пользователя может пройти авторизацию под идентификатором данного пользователя, если в качестве параметра $password введет часть sql запроса. Например, для пользователя ADMIN будут введены следующие параметры:

$login: 'ADMIN'
$password: '3' OR LOGIN IS NOT NULL

Тогда результирующий запрос будет иметь следующий вид:

SELECT USER_ID FROM ACCOUNT
WHERE (LOGIN =
'ADMIN') AND (PASSWORD = '3' OR LOGIN IS NOT NULL)

Если злоумышленник не знает даже имени пользователя, он все равно сможет получить идентификатор зарегистрированного пользователя для входа в систему при вводе следующего:

$login: 'ANY'
$password: '3') UNION SELECT USER_ID FROM ACCOUNT WHERE (USER_ID IS NOT NULL

В результате чего получится следующий запрос:

SELECT USER_ID FROM ACCOUNT
WHERE (LOGIN =
'ANY') AND (PASSWORD = '3') UNION SELECT USER_ID FROM ACCOUNT WHERE (USER_ID IS NOT NULL)

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

       UPDATE ACCOUNT
       SET PASSWORD = $new_password
       WHERE LOGIN = $login AND PASSWORD = $password

Параметры $login, $password и $new_password предназначены соответственно для вводимых пользователем текущих имени, пароля и нового пароля. При этом в случае не типизированных параметров пользователь имеет возможность изменить любое поле таблицы ACCOUNT. Например, для изменения баланса по своему счету пользователь вместо нового пароля введет следующее:

       PASSWORD, BALANCE = 100000.
Получим:
       UPDATE ACCOUNT
       SET PASSWORD = PASSWORD, BALANCE = 100000

В качестве заключительного примера рассмотрим наиболее показательную на мой взгляд уязвимость реальной программы, попавшей в поле моей деятельности. Программа была написана на Delphi и являлась клиентской частью для базы данных на основе сервера InterBase. В процессе запуска требовалось ввести имя оператора и пароль. Так как ни то, ни другое известно не было, я ввел троекратное 'A' в оба поля. SQL monitor показал следующий вызов с пустым результатом:

      SELECT * FROM PROCPSW(4, "AAA", "aaa")

Из этого можно сделать вывод, что PROCPSW вероятнее всего процедура проверки соответствия имени оператора и пароля. Также очевидно, что мой ввод передается не через типизированные параметры, а является частью строки запроса и ограничен двойными кавычками. И наконец, главное - пустой результат с двумя полями I0014, S0112, т.е. отсутствие набора возвращаемого данных, что скорее всего является признаком неудачной попытки авторизации. Исходя из этого можно предположить, что в случае возвращения какого либо набора данных из запроса, авторизация будет признана успешной.

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

aaa") union select 12 as i0014, cast("1223" as char(50)) as s0112 from RDB$RELATIONS where ("1"="1

Эта строка явилась часть запроса:

      SELECT * FROM PROCPSW(4, "AAA", "aaa")
      union select 12 as i0014, cast("1223" as char(50)) as s0112
      from RDB$RELATIONS where ("1"="1
")

Таким образом, была осуществлена подмена существования возвращаемого набора данных в результате вызова SELECT * FROM PROCPSW(4, "AAA", "aaa"). Здесь RDB$RELATIONS - системная таблица InterBase, которая использовалась в качестве таблицы, гарантировано имеющей записи.

Все описанные уязвимости были заложены в клиентских частях приложений. Но приведенные атаки можно реализовать без всяких ухищрений, если имеется доступ помимо клиентского приложения, т.е. прямой доступ к SQL серверу и соответствующие права. В дополнении к этому атакующий может использовать различные уязвимости SQL сервера, которые можно поделить на 2 класса:

  1. ошибки реализации SQL сервера;
  2. неправильное администрирование.

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

1. Ошибки в механизме контроля прав доступа и аутентификации пользователей

При наличии таких ошибок существует возможность преодоления механизмов разграничения доступа SQL сервера. Поясним это на реальном примере. В июле 2000г. была обраружена уязвимость в MS SQL Server 7.0, позволяющая любому пользователю запускать хранимые процедуры, владельцем которых является представитель роли sysadmin [3]. Оказалось, что SQL Server не выполняет проверку доступа, если хранимая процедура вызывается из другой временной (созданной только на время сессии текущего пользователя) хранимой процедуры. При этом атакующий может выполнять все системные хранимые процедуры, так как их владельцем является администратор.

Например, следующия последовательность команд позволяет создать новый логин SQL cсервера:

CREATE PROCEDURE #CreateLogin AS EXEC sp_addlogin 'newlogin'
EXEC #NewLogin

Также SQL Server 7.0 не выполняет проверку прав доступа при соединении посредством ODBC и OLE DB и использовании неинтегрированного логина [4]. Эта уязвимость позволяет любому пользователю выполнять любые операции над объектами базы данных и выполнять команды на самом сервере. Например, следуюший запрос выводит список всех сервисов на сервере inspector, где установлен SQL Server:

SELECT * FROM OPENROWSET('SQLOLEDB',
            'Trusted_Connection=yes;Data source=inspector',
            'SET FMTONLY OFF execute master..xp_cmdshell "net start"')

2. Подверженность DOS (Denial-of-Service) атакам или атакам на отказ SQL сервера в обслуживании

Здесь примером могут служить ряд уязвимостей в Listener Oracle 8 [5].

К числу уязвимостей, определемых некорректным администрированием, можно отнести следующие.

  1. Прежде всего это использование слабых паролей. Так, например, хорошо известны пароли для административных аккаунтов, устанавливаемые по умолчанию, которые необходимо сменить после инсталяции сервера:
    • Oracle: пользователь SYSTEM - пароль manager, пользователь SYS - пароль change_on_install;
    • MS SQL: логин SA - пустой пароль;
    • Interbase: пользователь SYSDBA - пароль masterkey.
  2. Использование прав администратора при создании и проектировании базы данных.
  3. Использование незащищенных протоколов. Для серверов с особо критичными данными, где не используется промежуточный сервер приложений, следует применять протоколы с криптографической защитой.
  4. Назначение чрезмерных прав пользователям.
  5. Несвоевременная установка "заплаток".
  6. Некорректные права доступа к локальным файлам и конфигурации сервера.

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

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

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

Строковые параметры следует ограничить апострофами и удвоить любой апостроф внутри строки. Например, строка '3' OR LOGIN IS NOT NULL из предыдущего примера после данной обработки будет выглядеть следующим образом: '''3'' OR LOGIN IS NOT NULL' и будет воспринята сервером как строка символов, а не как часть запроса.

В числовых параметрах следует удалять все символы кроме цифр. Если необходимо работать с отрицательными числами, также оставлять одиночный тире '-', так как '--' служит признаком начала комментария для Oracle и MSSQL. Таким образом, числовой параметр -15-758 после обработки будет выглядеть как -15. Следует отметить, что данной обработке следует подвергать все без исключения параметры, введенные пользователем.

В-третьих, необходимо использовать средства разграничения доступа предоставляемые SQL сервером. Так как меры защиты описанные выше должны быть предприняты разработчиками на множестве различных клиентских приложений, то при относительно большом проекте существует очень большая вероятность, что какой-либо ввод пользователя останется необработанным. Также вполне вероятно, что на web сервере остались стандартные скрипты примеров работы c базой данных и прочее. В этой ситуации дополнительной мерой защиты послужит использование средств безопасности SQL сервера. SQL сервер предоставляет двухуровневую систему разграничения доступа:

  • первый уровень - разграничение прав пользователя на доступ к различным объектам базы данных;
  • второй уровень - разграничение доступа и скрытие бизнес-логики посредством представлений и хранимых процедур.
Таким образом, для критичных к безопасности приложений, впрочем и для всех остальных, обязательно следует придерживаться следующих правил:
  • Никогда не давать права пользователю на прямой доступ к таблицам. Речь идет о всех правах: SELECT, UPDATE, INSERT, DELETE.
  • Пользователь должен получать доступ к базе данных только посредством представлений и хранимых процедур, права на выполнения которых и должны быть единственными правами пользователя. В совокупности с предыдущей данная мера помимо скрытия бизнес-логики позволит избежать несанкционированных пакетных запросов, подобных: DELETE FROM TABLE.
Также в случае выборки данных целесообразно применение хранимых процедур, позволяющих возвращать наборы данных, например хранимые процедуры MSSQL и InterBase. Для Oracle следует использовать представления, зависящие от переменных параметров, устанавливаемых посредством хранимых процедур.

В-четвертых, для особо критичных к безопасности приложений пользователь не должен иметь возможности прямого соединения с SQL сервером. Для web приложений можно прибегнуть к следующим мерам:

  • настроить SQL сервер на работу с протоколами отличными от TCP/IP
  • запретить к нему доступ из внешнего мира посредством межсетевого экрана
Для обычных приложений можно воспользоваться промежуточным сервером на основе multi layer технологий таких как MIDAS.

И наконец, наведите порядок на вашем SQL сервере. Проверьте всех пользователей, их права на различные объекты, установите "заплатки" и т.п. Здесь большую помощь окажет DataBase Scanner от Internet Security Systems. DataBase Scanner это автоматизированное средство анализа и аудита безопасности SQL сервера. Все, что вам потребуется сделать это указать тип сервера (поддерживаются Oracle, MSSQL, Sybase), его адрес, имя и пароль пользователя, которым будет пользоваться сканер для анализа. По окончании всех проверок вы получите детальный многостраничный отчет о всех найденных уязвимостях. DataBase Scanner можно скачать с http:\\www.iss.net.

Итак, приведенные примеры показывают, что при разработке приложений, работающих с базами данных, следует уделять больше внимания безопасности. С этими проблемами вам все равно рано или поздно придется столкнуться. Так не лучше ли рано?

Использованные источники:

  1. rain forest puppy, NT Web Technology Vulnerabilities, Phrack 54, 1998, декабрь, http://phrack.org/show.php?p=54&a=8
  2. rain forest puppy, How I hacked PacketStorm, Bugtraq, http://www.securityfocus.com/archive/1/44863
  3. Microsoft Security Bulletin (MS00-048) "Stored Procedure Permissions" Vulnerability http://www.microsoft.com/technet/security/bulletin/ms00-048.asp
  4. Microsoft Security Bulletin (MS00-014) "SQL Query Abuse" Vulnerability http://www.microsoft.com/technet/security/bulletin/ms00-014.asp

 
© 2003-2008 Andrey Kolishchak
Designed by a.shoshin