ORACLE使用透明数据库加密
--官网文档:https://www.oracle.com/technetwork/cn/tutorials/tde-096009-zhs.html#t
Oracle 数据库 10g 第 2 版透明数据库加密功能简化了信用卡号码以及社会安全保险号等机密个人信息的加密。 使用透明数据加密功能,不必将加密例程嵌套到现有应用程序中,显著降低了加密的成本和复杂性。
大多数加密解决方案都需要在应用程序代码中对调用加密函数。 这样做开销很高,因为它通常需要深入了解应用程序并要能够编写和维护软件。
通过 SQL 执行的应用程序逻辑不需要进行更改,仍能正常运行。 Oracle 数据库在将信息写入磁盘之前将自动对数据进行加密,随后的选择操作将透明地解密数据,因此应用程序将继续正常地运行。
设置加密密钥:Oracle 透明数据加密提供了实施加密所必需的关键管理基础架构。 加密的工作原理是将明文数据以及秘密(称作密钥)传递到加密程序中。 加密程序使用提供的密钥对明文数据进行加密,然后返回加密数据。 以往,创建和维护密钥的任务由应用程序完成。 Oracle 透明数据加密通过为整个数据库自动生成一个万能密钥解决了此问题。 在启动 Oracle 数据库时,管理员必须使用不同于系统口令或 DBA 口令的口令打开一个 Oracle Wallet 对象。 然后,管理员对数据库万能密钥进行初始化。 万能密钥是自动生成的。
性能:由于索引数据未被加密,因此加密通常会影响现有的应用程序索引。 Oracle 透明数据加密对与给定应用程序表关联的索引值进行加密。 这意味着应用程序中的相等搜索对性能的影响很小,甚至没有任何影响。 例如,假设应用程序 PERSON ID 存在一个索引,并且此应用程序执行以下语句:
--Oracle 数据库将使用现有的应用程序索引,尽管 PERSON ID 信息已经在数据库中加密。
SQL> Select rating from credit where person id = '23590';
创建一个包含加密列的表,为加密列创建一个索引,并授予访问某个用户的列的权限, 然后,您将创建一个包含对加密数据进行相应访问控制的函数,随后通过 VPD 策略应用该函数。
透明数据加密在数据写入磁盘之前对其进行加密,并在读取该数据时将其解密。 这对所有使用 SQL 层的应用程序都是透明的。 因此,无法轻松地验证数据是否真正被加密。 由于 Oracle LogMiner 对写入磁盘的数据进行记录,因此您可以访问该信息。
准备用于加密的数据库
在本部分内容中,您将更新 sqlnet.ora、创建一个加密钱夹 (ewallet.p12)、打开此钱夹并为 TDE 创建万能密钥。执行以下操作:
1.您需要更新 sqlnet.ora 文件以包含一个 ENCRYPTED_WALLET_LOCATION 条目。打开一个终端窗口,然后输入以下命令:
cd $ORACLE_HOME/network/admin
gedit sqlnet.ora
--将以下条目添加到文件末尾,可以为加密钱夹选择任何目录,但路径不应指向在数据库安装过程中创建的标准模糊钱夹 (cwallet.sso):
ENCRYPTION_WALLET_LOCATION=
(SOURCE=(METHOD=FILE)(METHOD_DATA=
(DIRECTORY=/u01/app/oracle/product/10.2.0/db_1/)))
2.接下来,您需要打开钱夹并创建万能加密密钥。从终端窗口中,输入以下命令:
cd /home/oracle/wkdir
sqlplus /nolog
@tde00_dbsetup
--只有拥有“alter system”权限的用户才能创建万能密钥或打开钱夹;万能密钥只能创建一次,除非您想要使用新的加密密钥重新加密数据
connect / as sysdba
--如果指定的目录中不存在加密钱夹,则将创建加密钱夹 (ewallet.p12)、打开此钱夹并创建/重新创建 TDE 的万能密钥; 如果指定目录中存在加密钱夹,则将打开此钱夹并创建/重新创建 TDE 的万能密钥。
alter system set key identified by "welcome1";
--需要打开钱夹(它在您关闭数据库时已经关闭),但您不希望创建一个新的万能密钥
alter system set wallet open identified by "welcome1";
由于每个表都有各自的加密密钥,因此万能加密密钥是必需的。这些列密钥存储在数据库中。 由于钱夹只能存储有限数目的密钥,并且可伸缩性不高,因此使用万能密钥加密列密钥。 这样,您便可以拥有所需数量的列密钥,并且钱夹中只存储少量的万能密钥(包括过期密钥,当您某一天从旧的备份磁带解密数据时可能需要它)。 默认情况下,以上命令使用 192 位的高级加密标准 (AES192) 生成一个密钥。 也可以使用 3DES,或使用较小或较大的 AES 加密位数。
--创建一个包含加密列的表,为加密列创建索引并授予数据的访问权限。执行以下操作:
1.首先需要创建一些用户。从 SQL*Plus 会话中,执行以下脚本:
@tde01_crusers
connect system/oracle
prompt Create users: JKING, LSMITH and LDORAN
grant connect to JKING identified by welcome1;
grant connect, DBA to LSMITH identified by welcome1;
grant connect to LDORAN identified by welcome1;
注意: 您已经授予了 LSMITH DBA 角色,以演示 TDE 对相等性搜索索引的支持。 因此,dbms_xplan 的输出应包含 INDEX RANGE SCAN。
2.接下来,您将创建一个表,其中包含一个用于存储加密(默认为 AES192)信用卡信息的列。 由于 credit_card_number 将有一个索引,因此未指定 SALT,当对加密值进行 salt 处理时将不会生成索引。从 SQL*Plus 会话中,执行以下脚本:
@tde02_crtabl
connect oe/oe
create table cust_payment_info
(first_name varchar2(11),
last_name varchar2(10),
order_number number(5),
credit_card_number varchar2(16) ENCRYPT NO SALT,
active_card varchar2(3));
3.现在,可以向刚刚创建的表中添加一些数据。从 SQL*Plus 会话中,执行以下脚本:
@tde03_poptabl
insert into cust_payment_info values
('Jon', 'Oldfield', 10001, '5446959708812985','YES');
insert into cust_payment_info values
('Chris', 'White', 10002, '5122358046082560','YES');
insert into cust_payment_info values
('Alan', 'Squire', 10003, '5595968943757920','YES');
insert into cust_payment_info values
('Mike', 'Anderson', 10004, '4929889576357400','YES');
insert into cust_payment_info values
('Annie', 'Schmidt', 10005, '4556988708236902','YES');
insert into cust_payment_info values
('Elliott', 'Meyer', 10006, '374366599711820','YES');
insert into cust_payment_info values
('Celine', 'Smith', 10007, '4716898533036','YES');
insert into cust_payment_info values
('Steve', 'Haslam', 10008, '340975900376858','YES');
insert into cust_payment_info values
('Albert', 'Einstein', 10009, '310654305412389','YES');
4.为提高性能,您将为信用卡号码创建一个索引。从 SQL*Plus 会话中,执行以下脚本:
@tde04_cridx
create index cust_payment_info_idx on cust_payment_info (credit_card_number);
5.需要向用户授予客户付款信息表的访问权限。 在本实例中,LSMITH 是唯一一个可以更新此信息的用户。 其他用户只能查看它。从 SQL*Plus 会话中,执行以下脚本:
@tde05_grant_access
grant select on oe.CUST_PAYMENT_INFO to LDORAN;
grant select, update on oe.CUST_PAYMENT_INFO to LSMITH;
grant select on oe.CUST_PAYMENT_INFO to JKING;
在本部分中,您将以 LSMITH 的身份对表进行更改。执行以下操作:
1.由于您授与了 LSMITH 更新访问权限,因此能够进行更改。从 SQL*Plus 会话中,执行以下脚本:
@tde06_make_update
prompt *** Connect as Lindsay Smith (Card_V)
conn LSMITH/welcome1;
update oe.CUST_PAYMENT_INFO set ACTIVE_CARD='NO'
where CREDIT_CARD_NUMBER='4556988708236902';
2.要查看执行计划,请执行以下脚本:
@tde06a_review_xplan
select * from table (dbms_xplan.display_cursor);
3.表 user_encrypted_columns 将通知您哪个列已经加密以及它的加密算法。从 SQL*Plus 会话中,执行以下脚本:
@tde07_select_encrypt_col
connect oe/oe
col TABLE_NAME format a18;
col COLUMN_NAME format a19;
col ENCRYPTION_ALG format a17;
select * from user_encrypted_columns;
添加并应用 VPD 策略
由于加密并不替换相应的访问控制,因此您将使用一个非常简单的 VPD 策略限制行的访问权限。 首先,您将检查登录到数据库的用户是否是员工,然后将按信用卡号码限制 oe.cust_payment_info 的访问权限:
Card_A 从“34”或“37”开始 Janette King
Card_V 从“4”开始 Lindsay Smith
Card_M 从“5”开始 Louise Doran
如果查看加载到表中的数据,则会看到,授权用户将无法选择与“Albert Einstein”关联的信用卡号码。 只有避开访问控制策略的入侵者或不受这些策略约束的管理用户才可以选择它。 这可以对该表进行高度集中的审计。
执行以下操作:
1.首先需要创建包含信用卡和员工验证所需逻辑的表。从 SQL*Plus 会话中,执行以下脚本:
@tde08_crfunction
connect system/oracle;
prompt
prompt *** Create policy function to create the where-clause:
create or replace function f_policy_oe_cust_payment_info
-- Function must have the following parameters
(schema in varchar2, tab in varchar2)
-- Function will return a string that is used as a WHERE clause
return varchar2
as
v_manager_id number:=0;
is_employee number:=0;
v_user varchar2(20);
out_string varchar2(70) default '1=2 ';
begin
-- get session user
v_user := lower(sys_context('userenv','session_user'));
-- Is the user an employee?
begin
select manager_id into v_manager_id
from hr.employees
where lower(email) = v_user;
is_employee:=1;
exception
when no_data_found then
is_employee:=2;
end;
-- create where clause when user is authorized to see parts of the table
if is_employee=1 and lower(v_user)='jking' and v_manager_id=146 then
out_string := out_string ||'or CREDIT_CARD_NUMBER like ''34%''
or CREDIT_CARD_NUMBER like ''37%''';
elsif is_employee=1 and lower(v_user)='lsmith' and v_manager_id=146 then
out_string := out_string ||'or CREDIT_CARD_NUMBER like ''4%''';
elsif is_employee=1 and lower(v_user)='ldoran' and v_manager_id=146 then
out_string := out_string ||'or CREDIT_CARD_NUMBER like ''5%''';
end if;
return out_string;
end;
/
2.现在,可以将该策略添加到 cust_payment_info 表中。从 SQL*Plus 会话中,执行以下脚本:
@tde09_addpolicy
prompt
prompt *** Add policy to 'oe.cust_payment_info' table:
begin
dbms_rls.add_policy('oe','cust_payment_info','ac_cust_payment_info',
'system','f_policy_oe_cust_payment_info', policy_type => dbms_rls.context_sensitive);
end;
/
--测试策略,将以每个员工的身份连接以查看该策略是否起作用。 有三个方面可以体现 TDE 的真正透明性:
加密列已经使用索引
即使加密存储了信用卡号码,VPD 策略中的 where 子句仍搜索明文格式的号码并检索相应的行。
对三名员工可见的行包含明文格式的信用卡号码。 通常,员工甚至不知道已经加密存储了数据。
1.您将首先以 Janette King 的身份连接。 她可以访问从“34”或“37”开始的 Card_A。从 SQL*Plus 会话中,执行以下脚本:
@tde10_testpolicy_jking
prompt
prompt *** Connect as Janette King (Card_A)
conn JKING/welcome1;
col CREDIT_CARD_NUMBER heading Card_A format a18;
select * from oe.CUST_PAYMENT_INFO order by CREDIT_CARD_NUMBER;
2.您现在将以 Louise Doran 的身份连接。 她可以访问从“5”开始的 Card_M。从 SQL*Plus 会话中,执行以下脚本:
@tde11_testpolicy_ldoran
prompt
prompt *** Connect as Louise Doran (Card_M)
conn LDORAN/welcome1;
col CREDIT_CARD_NUMBER heading Card_M format a18;
select * from oe.CUST_PAYMENT_INFO order by CREDIT_CARD_NUMBER;
3.然后,您将以 Lindsay Smith 的身份连接。 她可以访问从“4”开始的 Card_V。从 SQL*Plus 会话中,执行以下脚本:
@tde12_testpolicy_lsmith
prompt
prompt *** Connect as Lindsay Smith (Card_V)
conn LSMITH/welcome1;
col CREDIT_CARD_NUMBER heading Card_V format a18;
select * from oe.CUST_PAYMENT_INFO order by CREDIT_CARD_NUMBER;
--使用 LogMiner 查看重做日志
由于 TDE 是在写入数据之前执行的并且对所有应用程序都是透明的,因此无法轻松地验证是否真正加密了数据。 由于 Oracle LogMiner 记录写入磁盘的数据,因此可以使用它查看日志文件中包含的内容。执行以下步骤:
1.从 SQL*Plus 会话中,执行以下脚本:
@tde13_logminer
connect / as sysdba;
alter database add supplemental log data;
REM select member as LOG_FILE_LOCATION from v$logfile;
EXECUTE DBMS_LOGMNR.ADD_LOGFILE
('+MY_DG2/racdb/onlinelog/group_3.263.562151437', DBMS_LOGMNR.NEW);
EXECUTE DBMS_LOGMNR.ADD_LOGFILE
('+MY_DG2/racdb/onlinelog/group_2.262.562151433', DBMS_LOGMNR.ADDFILE);
EXECUTE DBMS_LOGMNR.ADD_LOGFILE
('+MY_DG2/racdb/onlinelog/group_1.261.562151431', DBMS_LOGMNR.ADDFILE)
prompt start LogMiner:
EXECUTE DBMS_LOGMNR.START_LOGMNR
(options => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG + DBMS_LOGMNR.COMMITTED_DATA_ONLY);
select sql_redo from v$logmnr_contents where
table_name = 'CUST_PAYMENT_INFO' and operation='INSERT';
--LogMiner 不支持加密数据,因此 credit_card_number 列中的加密值显示为 Unsupported Type。
--在无加密列的情况下重新创建表,为显示在未对列进行加密的情况下将看到的内容之间的差异,要删除该表并在无加密列的情况下重新创建它。执行以下操作:
1.从 SQL*Plus 会话中,执行以下脚本:
@tde14_crtabl2
connect oe/oe
drop table cust_payment_info;
create table cust_payment_info
(first_name varchar2(11),
last_name varchar2(10),
order_number number(5),
credit_card_number varchar2(20),
active_card varchar2(3));
insert into cust_payment_info values
('Jon', 'Oldfield', 10001, 5446959708812985,'YES');
insert into cust_payment_info values
('Chris', 'White', 10002, 5122358046082560,'YES');
insert into cust_payment_info values
('Alan', 'Squire', 10003, 5595968943757920,'YES');
insert into cust_payment_info values
('Mike', 'Anderson', 10004, 4929889576357400,'YES');
insert into cust_payment_info values
('Annie', 'Schmidt', 10005, 4556988708236902,'YES');
insert into cust_payment_info values
('Elliott', 'Meyer', 10006, 374366599711820,'YES');
insert into cust_payment_info values
('Celine', 'Smith', 10007, 4716898533036,'YES');
insert into cust_payment_info values
('Steve', 'Haslam', 10008, 340975900376858,'YES');
insert into cust_payment_info values
('Albert', 'Einstein', 10009, 310654305412389,'YES');
create index cust_payment_info_idx on cust_payment_info (credit_card_number);
grant select on oe.CUST_PAYMENT_INFO to LDORAN;
grant select, update on oe.CUST_PAYMENT_INFO to LSMITH;
grant select on oe.CUST_PAYMENT_INFO to JKING;
prompt *** Connect as Lindsay Smith (Card_V)
conn LSMITH/welcome1;
update oe.CUST_PAYMENT_INFO set ACTIVE_CARD='NO'
where CREDIT_CARD_NUMBER=4556988708236902;
--查看重做日志,重新运行 logminer 脚本来查看它所包含的内容
1.从 SQL*Plus 会话中,执行以下脚本:
@tde15_logminer2
connect / as sysdba;
EXECUTE DBMS_LOGMNR.ADD_LOGFILE
('+MY_DG2/racdb/onlinelog/group_3.263.562151437', DBMS_LOGMNR.NEW);
EXECUTE DBMS_LOGMNR.ADD_LOGFILE
('+MY_DG2/racdb/onlinelog/group_2.262.562151433', DBMS_LOGMNR.ADDFILE);
EXECUTE DBMS_LOGMNR.ADD_LOGFILE
('+MY_DG2/racdb/onlinelog/group_1.261.562151431', DBMS_LOGMNR.ADDFILE)
prompt start LogMiner:
EXECUTE DBMS_LOGMNR.START_LOGMNR
(options => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG + DBMS_LOGMNR.COMMITTED_DATA_ONLY);
select sql_redo from v$logmnr_contents where
table_name = 'CUST_PAYMENT_INFO' and operation='INSERT';
--该列未加密,且 LogMiner 显示已写入磁盘的名文数据。
--要清理环境,请执行以下步骤:
1.从 SQL*Plus 会话中,执行以下脚本:
@tde16_cleanup
connect system/oracle
drop user JKING cascade;
drop user LSMITH cascade;
drop user LDORAN cascade;
drop function f_policy_oe_cust_payment_info;
connect oe/oe
drop table cust_payment_info;
exit;