Oracle 12c Data Redaction. Сокрытие информации от непривилегированных пользователей

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

    Начиная с версии 8 в Oracle Database существует Virtual Private Database (VPD). VPD позволяет, в зависимости от разных условий, скрывать часть записей таблицы или view от пользователей. Недостаток проистекает из достоинств — сокрытие возможно только полных записей. Делается это через добавление дополнительных условий в where часть sql запроса.

    В Oracle 12c добавлена возможность изменять выдаваемые sql запросом значения полей (полностью или частично), в зависимости от условий. Эта возможность получила название Oracle Data Redaction и состоит в применении специальных policy.

    Все управление Data Redaction сосредоточено в пакете DBMS_REDACT. Важно то, что для применения policy к объекту, не нужно иметь какого либo доступа к самому объекту. Пользователи обладающие привилегией EXEMPT REDACTION POLICY не подпадают под действие данного механизма.

    Рассмотрим использование Data Redaction на следующем примере:

    Есть таблица CLIENT_INFO содержащая персональные данные — дату рождения, телефон, email и номер кредитной карты. В базе существует роль R_VIP, обладатели которой видят полную информацию. Остальные должны видеть отредактированный вариант.
    таблица и тестовая запись
    CREATE TABLE CLIENT_INFO 
    (
      ID NUMBER,
      F_NAME VARCHAR2(64), /* фамилия*/
      NAME VARCHAR2(64),  /* имя */
      S_NAME VARCHAR2(64), /* отчество */ 
      BIRTHDAY DATE, /* дата рождения */
      PHONE VARCHAR2(32), /* телефон */
      EMAIL VARCHAR2(64), /* email */
      CCARD VARCHAR2(32), /* номер кредитной карты */
      CONSTRAINT "CLIENT_INFO_PK" PRIMARY KEY ("ID")
    );
    insert into CLIENT_INFO values(1, 'Иванов', 'Иван', 'Иванович', to_date('15-05-1986', 'DD-MM-YYYY'), '79763334589', 'ivan@dom2.ru', '5767881897856776');
    

    Создаем policy под названием redact_client_info и добавляем правило, по которому день в дате рождения заменяется на 1ое число:
    policy
    BEGIN
    DBMS_REDACT.ADD_POLICY(
         object_schema        => 'TEST',
         object_name          => 'CLIENT_INFO',
         column_name          => 'BIRTHDAY',
         policy_name          => 'redact_client_info',
         function_type        => DBMS_REDACT.PARTIAL, /* Частичное маскирование */
         function_parameters  => 'Md01Y', /* Маска изменений */
         expression           => 'SYS_CONTEXT(''SYS_SESSION_ROLES'',''R_VIP'') = ''FALSE''' /* Условие - замена при отсутствии роли R_VIP */
    );
    END;
    /
    

    Добавляем в policy условие показывать только 5 последних цифр телефона:
    policy
    BEGIN
    DBMS_REDACT.ALTER_POLICY(
         object_schema         => 'TEST',
         object_name           => 'CLIENT_INFO',
         column_name           => 'PHONE',
         policy_name           => 'redact_client_info',
         function_type         => DBMS_REDACT.REGEXP, /* Маскирование с помощью регулярного выражения */
         regexp_pattern	   => '\d+(\d{5})$', 
         regexp_replace_string => '******\1',
         regexp_position       => DBMS_REDACT.RE_BEGINNING,
         regexp_occurrence     => DBMS_REDACT.RE_ALL,
         expression            => 'SYS_CONTEXT(''SYS_SESSION_ROLES'',''R_VIP'') = ''FALSE''',
         action                => DBMS_REDACT.ADD_COLUMN
    );
    END;
    /
    

    Добавляем в policy условие скрывать домен email:
    policy
    BEGIN
    DBMS_REDACT.ALTER_POLICY(
         object_schema         => 'TEST',
         object_name           => 'CLIENT_INFO',
         column_name           => 'EMAIL',
         policy_name           => 'redact_client_info',
         function_type         => DBMS_REDACT.REGEXP,
         regexp_pattern	   => DBMS_REDACT.RE_PATTERN_EMAIL_ADDRESS, /* Используем готовое */
         regexp_replace_string => DBMS_REDACT.RE_REDACT_EMAIL_DOMAIN, /* Используем готовое */
         expression            => 'SYS_CONTEXT(''SYS_SESSION_ROLES'',''R_VIP'') = ''FALSE''',
         action                => DBMS_REDACT.ADD_COLUMN
    );
    END;
    /
    

    Ну и наконец маскируем номер кредитки:
    policy
    BEGIN
    DBMS_REDACT.ALTER_POLICY(
         object_schema         => 'TEST',
         object_name           => 'CLIENT_INFO',
         column_name           => 'CCARD',
         policy_name           => 'redact_client_info',
         function_type         => DBMS_REDACT.REGEXP,
         regexp_pattern	   => DBMS_REDACT.RE_PATTERN_CC_L6_T4,
         regexp_replace_string => DBMS_REDACT.RE_REDACT_CC_MIDDLE_DIGITS,
         expression            => 'SYS_CONTEXT(''SYS_SESSION_ROLES'',''R_VIP'') = ''FALSE''',
         action                => DBMS_REDACT.ADD_COLUMN
    );
    END;
    /
    

    Выполняем select от пользователя не имеющего роли R_VIP:
    SQL> select f_name, name, birthday, email, phone, ccard from test.client_info;
    
    F_NAME	   NAME       BIRTHDAY	      EMAIL	      PHONE	      CCARD
    ---------- ---------- --------------- --------------- --------------- ----------------
    Иванов     Иван       01-MAY-86       ivan@xxxxx.com  ******34589     576788XXXXXX6776
    

    И от пользователя обладающего этой ролью:
    SQL> select f_name, name, birthday, email, phone, ccard from test.client_info;
    
    F_NAME	   NAME       BIRTHDAY	      EMAIL	      PHONE	      CCARD
    ---------- ---------- --------------- --------------- --------------- ----------------
    Иванов     Иван       15-MAY-86       ivan@dom2.ru    79763334589     5767881897856776
    

    В результате в запросах видны все записи, но значения полей, для пользователя не имеющего роли R_VIP, изменены.

    Плюс использования Data Redaction в сохранении семантики данных, но при этом непригодности конкретных значений для несанкционированного использования.

    Важное дополнение


    В процecce дальнейшего обсуждения и исследования работы этого механизма выяснилось, что данные маскируются только при выводе. То есть в части where используются открытые данные. И как результат — можно получить все открытые данные подставив свою функцию в where:
    create table log (s varchar2(128));
    
    create or replace function f(ds varchar2) return varchar2 as
      PRAGMA AUTONOMOUS_TRANSACTION;
     begin
      insert into log values(ds);
      commit;
      return ds;
     end;
    
    SQL> select name, email from client_info c where f(email) like '%dom%';
    NAME	   EMAIL
    ---------- ---------------
    Иван       ivan@xxxxx.com
    
    SQL> select * from log;
    
    S
    ----------------
    ivan@dom2.ru
    ivan@gmail.com
    

    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 20
    • +1
      Очень крутая фича. Интересно, как быстро перекочует в прочие популярные базы.
      • 0
        Пока не совсем ясно, как защищаться от перебора через Select.
        Выбрали нужную запись с условием where Phone like '8%', потом подобрали like '89%' и т.д.
        Есть ли какие-то действенные варианты защиты?
        • 0
          Про методы защиты. Это не 100% защита, а способ затруднить доступ. Одно дело сделать select * и унести распечатку, и совсем другое сидеть и угадывать. При этом угадывать можно только на той же базе, нельзя унести замаскированное и на другой системе пытаться восстановить информацию.
          • 0
            Аудит, мониторинг, тут другого варианта вообще трудно придумать.
        • 0
          Можно конечно хранимок понаделать, но так да, элегантней
          • 0
            Допустим у нас есть хранимка скомпиленная из под r_vip, которая делает селект из этой таблицы. Если эту хранимку вызовет другой (не r_vip) он увидит полные данные?
            • 0
              Полные данные может увидеть только та сессия у которoй активна роль R_VIP. Даже владелец, без роли, полных данных не увидит.
              • 0
                А если например у нас хранимая процедура, где необходимо сделать например такой селект, но роли R_VIP у нас нет:

                select phone, ccard into v_phone, v_card from test.client_info where ccard = get_card_from_somewhere;

                Как такое реализовать?

                То есть вопрос, если у нас нет роли R_VIP то получается что и процедуры не видят полных данных?
                А если процедуры созданы под пользователем у которого есть роль R_VIP это нам тоже не поможет?
                • 0
                  Если роли нет, то увидеть полные данные невозможно. Если проверять в policy не роль, а CURRENT_USER и user создавший процедуру (с Definer Rights) имеет полный доступ, то и процедура будет иметь полный доступ.
            • 0
              можно ли производить какие-то операции над закрытыми данными: джоин, груп, конкат?
              • 0
                они будут участвовать во всех операциях как закрытые или открытые в зависимости от наличия роли. Но это никак не влияет на колонки не упомянутые в policy.
                • +1
                  мне любопытно в какой момент происходит redaction
                  пусть у меня нет этой роли, что будет если я скажу
                  … group by email… — и в базе есть ivan@dom2.ru и ivan@dom3.ru, который после redact неотличимы — получу я 2 строки или 1?
                  А если я использую wm_concat? В общем функция очевидно непростая, поэтмоу мне и интересно, какие у нее есть ограничения
                  • +1
                    Скорее всего данные маскируются при выборке результата из курсора, врядли oracle решился сломать внутренние механизмы. Это скорее для клонок не участвующих в групповых операциях.
                    SQL> select count(*), email from test.client_info group by email;
                    
                      COUNT(*) EMAIL
                    ---------- ---------------
                    	 1 ivan@xxxxx.com
                    	 1 ivan@xxxxx.com
                    
              • 0
                А это все дело на план будет влиять? Могут поехать все планы?
                • 0
                  Сбор статистики будет идти по реальным данным, предикаты работают с реальными данными и маскируются только при выводе.
                  SQL> select  name,  email  from client_info c where email like '%dom%';
                  NAME	   EMAIL
                  ---------- ---------------
                  Иван       ivan@xxxxx.com
                  
                  • 0
                    Ок, спасибо, теперь понятно :)
                • +2
                  Т.е. таки можно узнать любые отредактированные данные, хотя бы дихотомией — дело лишь в том, что их нельзя получить быстро, так?
                  • 0
                    Да, пытаться угадать — можно. И можно даже установить, правильно ли угадал:
                    SQL> select  name,  email  from client_info c where email = 'ivan@dom2.ru';
                    NAME	   EMAIL
                    ---------- ---------------
                    Иван       ivan@xxxxx.com
                    

                    Но это затруднительно, и для пресечения таких попыток можно использовать аудит.
                    • 0
                      Не сильно затруднительно. Можно угадывать «по букве». На каждую следующую цифру в телефоне в среднем 5 попыток.
                      И на что аудит? На Select?

                      А нет ли фичи, сделать так, чтобы запросы, в которых фигурируют УСЛОВИЯ на защищаемые колонки — не выполнялись?

                      • +1
                        FGA (fine grained audit) позволяет отслеживать select'ы по колонке, да еще и с анализом условий запроса. Не в этом дело. Обеспечение безопасности — это многоступенчатый процесс. Эта фича рассматривается как «дешевый» (в смысле ресурсов) вариант. Для усиленной защиты существует Transparent data encryption, когда данные хранятся в зашифрованном виде. Но естественно и ресурсов на encrypt/decrypt уходит существенно больше.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.