Управление ключами
Занимаясь блочным шифрованием, группа Джона ищет и полное решение для шифрования информации на основе пакета DBMS_CRYPTO. Самой большой проблемой в шифровании, объясняет Джон, является не генерация ключей или использование функций, а управление ключами, используемыми в процессе шифрования. Одни и те же ключи используются для шифрования и дешифрования данных, поэтому их нужно надежно охранять, чтобы защитить данные. В то же самое время, однако, приложения и пользователи должны иметь доступ к ключам, чтобы дешифровать данные для нормального использования. Эта проблема решается, поясняет Джон, выбором места хранения ключей и обеспечением гарантий, что они будут доступны только законным пользователям. Он предоставляет два варианта управления ключами:
- Использование одного и того же ключа для всех записей.
- Использование разных ключей для разных записей.
В варианте 1, продолжает Джон, для шифрования данных во всех строках используется один ключ. В этом случае для хранения ключа существует несколько вариантов:
В базе данных. Для хранения ключа может использоваться таблица ключей, принадлежащая специальному пользователю, который не является владельцем приложений. Джон написал простую функцию, которая всего лишь возвращает ключ в выходном параметре. Пользователи получают привилегии для выполнения этой процедуры, и никакие пользователи не получают каких-либо привилегий для доступа к таблице ключей. В этой функции имеется несколько проверок, обеспечивающих получение ключа только пользователями, которые имеют соответствующие привилегии. Эта функция - единственный источник для получения ключа, поэтому пользователей можно легко аутентифицировать и предоставлять им доступ к ключу. В файловой системе. Хранение ключа в базе данных защищает от большинства злоумышленников, но не от администраторов базы данных, которые могут иметь доступ к любой таблице. Кроме того, может быть очень сложно удостовериться в том, что пользователи, выполняющие запрос, действительно являются законными пользователями. Хранение ключа в файловой системе, к которой администратор базы данных не имеет доступа, например, на другом сервере, таком, как сервер приложений, может оказаться лучшей идеей. Тем не менее, это также означает, что если ключ пропадает из-за повреждения файловой системы, то зашифрованные данные пропадают навсегда. У пользователя. В третьем варианте, показывает Джон, можно разрешить пользователям хранить ключ, например, в карте памяти (мобильного устройства) или на клиентской машине. В этом случае никто кроме законных пользователей не сможет иметь доступ к уязвимым данным. Это особенно полезно в хранилищах данных, в которых зашифрованные данные регулярно посылаются пользователям, уже имеющим ключ. Если по пути данные будут похищены, конфиденциальная информацию будет по-прежнему защищена. Тем не менее, здесь самый высокий риск потери данных, поскольку пользователи чаще всего теряют ключи.
Джон предлагает использовать один ключ для всех записей с хранением его у пользователей для небольшого числа случаев, таких, как публикация итогового контента для различных пользователей, ранее получившим ключ.
Самый большой недостаток этого подхода - уязвимость ключа к воровству. Если ключ украден, все данные в базе данных поставлены под угрозу. Поэтому Джон предложил другой подход для защиты конфиденциальных данных в базах данных OLTP-систем. Такая база данных имеет таблицу ACCOUNT_MASTER, в которой хранится конфиденциальный элемент данных - имя владельца банковского счета (account holder). Столбец, содержащий это имя, ACC_NAME, должен быть зашифрован. Первичный ключ этой таблицы - ACCOUNT_NO. Таблица ACCOUNT_MASTER выглядит примерно так:
SQL> desc account_master
Name Null? Type ---------- -------- ------------ ACCOUNT_NO NOT NULL NUMBER ACC_NAME VARCHAR2(200) ACC_TYPE CHAR(1)
Джон предлагает использовать разные ключи для различных строк таблицы ACCOUNT_MASTER, что устраняет риск незащищенности данных на уровне всей базы данных, если ключ крадется. Он создает таблицу ACCOUNT_MASTER_ENC для хранения зашифрованных данных (имена владельцев счетов) и другую таблицу для хранения ключей, использованных для шифрования. Эти таблицы - ACCOUNT_MASTER_ENC и ACCOUNT_MASTER_KEYS - выглядят примерно так:
SQL> desc account_master_enc
Name Null? Type ------------ -------- -------- ACCOUNT_NO NOT NULL NUMBER ACC_NAME_ENC RAW(2000)
SQL> desc account_master_keys
Name Null? Type ---------- -------- --------- ACCOUNT_NO NOT NULL NUMBER KEY NOT NULL RAW(2000)
Затем Джон создает представление VW_ACCOUNT_MASTER, показанное на листинге 4, чтобы соединить эти три таблицы для получения расшифрованных данных. Он указывает на строку 8 листинга 4, в которой данные расшифровываются с помощью функции GET_DEC_VAL, рассмотренной выше. Эта функция возвращает данные типа VARCHAR2, поэтому они показываются как столбец VARCHAR2(2000); функция CAST в строке 7 преобразует их в данные типа VARCHAR2(20).
Это представление, не таблица, как раз есть то, что предоставляется для доступа другим пользователям. Джон создает публичный синоним ACCOUNT_MASTER, указывающий на представление VW_ACCCOUNT_MASTER, а не на таблицу с таким же именем как у синонима.
Листинг 4. Представление VW_ACCOUNT_MASTER
1 create or replace view 2 vw_account_master 3 as 4 select 5 m.account_no as account_no, 6 m.acc_type as acc_type, 7 cast ( 8 get_dec_val (e.acc_name_enc, k.key) 9 as varchar2(20)) as acc_name 10 from 11 account_master m, 12 account_master_enc e, 13 account_master_keys k 14 where 15 k.account_no = e.account_no 16* and m.account_no = e.account_no;
Публичный синоним ACCOUNT_MASTER указывает на это представление, поэтому пользователи могут извлекать из него данные. Но, спрашивает разработчик Джилл, как же пользователи смогут манипулировать данными в таблице ACCOUNT_MASTER? Используя триггер INSTEAD OF (см. листинг 5), поясняет Джон. Этот триггер модифицирует реальные таблицы, когда пользователи вставляют строки в представление или обновляют его.
Листинг 5. Триггер INSTEAD OF для представления
1 create or replace trigger io_vw_acc_master 2 instead of insert or update on 3 vw_account_master 4 for each row 5 declare 6 l_key raw(2000); 7 begin 8 if (inserting) then 9 l_key := dbms_crypto.randombytes (128); 10 insert into account_master 11 (account_no, acc_type, acc_name) 12 values 13 ( 14 :new.account_no, 15 :new.acc_type, 16 :new.acc_name 17 ); 18 insert into account_master_enc 19 (account_no, acc_name_enc) 20 values 21 ( 22 :new.account_no, 23 get_enc_val ( 24 :new.acc_name, 25 l_key ) 26 ); 27 insert into account_master_keys 28 (account_no, key) 29 values 30 ( 31 :new.account_no, 32 l_key 33 ); 34 else 35 select key 36 into l_key 37 from account_master_keys 38 where account_no = :new.account_no; 39 update account_master 40 set acc_name = :new.acc_name, 41 acc_type = :new.acc_type 42 where account_no = :new.account_no; 43 update account_master_enc 44 set acc_name_enc = 45 get_enc_val (:new.acc_name, l_key) 46 where account_no = :new.account_no; 47 end if; 48* end;
Пользователи не могут видеть данные непосредственно в таблицах, а только через представление, поэтому информация защищена. Чтобы показывать только зашифрованные данные, защищая исходное содержимое, можно создавать различные представления базовой таблицы.
Содержание раздела