Oracle 在 23c 版本中增加了创建只读用户的功能。这可以通过使用新的 ALTER USER 或 CREATE USER 语法,并包含 READ ONLY 或 READ WRITE 子句来实现。让我们通过一个简单的演示来了解这个新特性。首先,连接到 23c 数据库:
SQL> sho user
USER is "SYS"
SQL> select banner from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 23c Free, Release 23.0.0.0.0 - Developer-Release
SQL>
接下来,我们可以创建一个示例用户 VV,并授予一些进行读和写的权限:
SQL> create user vv identified by vv;
User created.
SQL> grant create session to vv;
Grant succeeded.
SQL> grant select,insert on orablog.customer to vv;
Grant succeeded.
SQL> grant execute on orablog.custa to vv;
Grant succeeded.
SQL>
然后,以新用户身份连接并测试我们可以进行读和写操作:
SQL> connect vv/vv@//192.168.56.18:1521/freepdb1
Connected.
SQL>
-- test
SQL> select count(*) from orablog.customer;
COUNT(*)
----------
3
SQL> select * from orablog.customer;
FULLNAME FIRSTNAME
------------------------------ ------------------------------
LASTNAME
------------------------------
Pete Finnigan Pete
Finnigan
Zulia Finnigan Zulia
Finnigan
Eric Finnigan Eric
Finnigan
SQL> insert into orablog.customer (fullname,firstname,lastname) values ('EmilFinnigan','Emil','Finnigan');
1 row created.
SQL> set serveroutput on
SQL> exec orablog.custa('Finnigan');
name:=[Pete Finnigan]
name:=[Zulia Finnigan]
PL/SQL procedure successfully completed.
SQL>
现在,我们以 SYS 用户身份连接并改变我们的示例用户 VV 为只读:
SQL> connect sys/oracle@//192.168.56.18:1521/freepdb1 as sysdba
Connected.
SQL> alter user vv read only;
User altered.
SQL>
出于兴趣,这个只读设置也可以在用户创建时完成:
SQL> sho user
USER is "SYS"
SQL> create user vx identified by vx read only;
User created.
SQL>
以 VV 用户身份连接并重新执行 select 语句:
SQL> connect vv/vv@//192.168.56.18:1521/freepdb1
Connected.
SQL> select count(*) from orablog.customer;
COUNT(*)
----------
4
SQL> select * from orablog.customer;
FULLNAME FIRSTNAME
------------------------------ ------------------------------
LASTNAME
------------------------------
Pete Finnigan Pete
Finnigan
Zulia Finnigan Zulia
Finnigan
EmilFinnigan Emil
Finnigan
FULLNAME FIRSTNAME
------------------------------ ------------------------------
LASTNAME
------------------------------
Eric Finnigan Eric
Finnigan
SQL>
这些操作都是正确的。如果我们尝试一个插入语句,VV 有权限执行:
SQL> insert into orablog.customer(fullname,firstname,lastname) values ('TestUser','Test','User');
insert into orablog.customer(fullname,firstname,lastname) values ('TestUser','Test','User')
*
ERROR at line 1:
ORA-28194: Can perform read operations only
SQL>
所以,很好,只读功能按预期工作,即使我们有权限在该表上执行插入操作,我们也不能执行 INSERT。
现在尝试 PL/SQL 过程:
SQL> set serveroutput on
SQL> exec orablog.custa('Finnigan');
name:=[Pete Finnigan]
name:=[Zulia Finnigan]
PL/SQL procedure successfully completed.
SQL>
嗯,我们只对 ORABLOG.CUSTOMER 进行读操作,而不是插入,但我们执行了 PL/SQL。让我们尝试一个不同的例子:
SQL> exec dbms_output.put_line('Test test'); Test test PL/SQL procedure successfully completed. SQL>
嗯,我们可以运行 PL/SQL,所以对我来说它不仅仅是只读,我们可以执行 EXECUTE?
我们可以将普通用户设置为只读吗?连接到 PDB 并尝试:
SQL> connect sys/oracle@//192.168.56.18:1521/freepdb1 as sysdba
Connected.
SQL> alter session set container=cdb$root;
Session altered.
SQL> create user c##vv identified by c##vv read only;
create user c##vv identified by c##vv read only
*
ERROR at line 1:
ORA-28192: Enabling or disabling read-only property is not allowed on a common
user.
SQL>
所以我们不能给普通用户添加只读属性,尽管一个只读的普通用户可能很有用。
我们可以通过 PL/SQL 执行插入操作吗?
SQL> sho user
USER is "SYS"
SQL> grant create procedure to vv;
Grant succeeded.
SQL> connect vv/vv@//192.168.56.18:1521/freepdb1
Connected.
SQL> create procedure run as
2 begin
3 insert into orablog.customer(fullname,firstname,lastname) values ('TestName','Test','Name');
4 end;
5 /
create procedure run as
*
ERROR at line 1:
ORA-28194: Can perform read operations only
SQL>
非常有趣。我们不能创建一个过程,因为我们是只读的,因为过程被写入数据库,违反了只读规则。我们可以创建一个动态过程吗?
SQL> get dyn.sql
1 -- test dyn proceudre to test read only
2 -- --------------------------------------------------
3 -- Create the "procedure"
4 -- --------------------------------------------------
5 var lv_str varchar2(2000);
6 var curnum number;
7 -- null the string
8 exec :lv_str:='';
9 declare
10 begin
11 :curnum:=dbms_sql.open_cursor;
12 dbms_sql.parse(:curnum,'declare bv varchar2(2000):=:ipstr; begin dbms_output.put_line(bv);end;',dbms_sql.native);
13 end;
14 /
15 print curnum
16 -- -------------------------------------------------------
17 -- end of declaring the "procedure"
18 -- -------------------------------------------------------
19 -- -------------------------------------------------------
20 -- Call the dynamic procedure
21 -- -------------------------------------------------------
22 -- prime the string to print it
23 exec :lv_str:='hello world';
24 declare
25 n number;
26 begin
27 -- bind the variable
28 dbms_sql.bind_variable(:curnum,':ipstr',:lv_str);
29 n:=dbms_sql.execute(:curnum);
30 end;
31 /
32 -- --------------------------------------------------------
33 -- End of dynamic procedure call
34* -- --------------------------------------------------------
35 .
SQL>
SQL> @dyn
PL/SQL procedure successfully completed.
PL/SQL procedure successfully completed.
CURNUM
----------
1923939752
PL/SQL procedure successfully completed.
hello world
PL/SQL procedure successfully completed.
SQL>
所以我们可以绕过创建过程的限制,但我们能在这个过程里执行插入操作吗?让我们修改 dyn.sql:
SQL> get dyn.sql
1 -- test dyn proceudre to test read only
2 -- --------------------------------------------------
3 -- Create the "procedure"
4 -- --------------------------------------------------
5 var curnum number;
6 declare
7 begin
8 :curnum:=dbms_sql.open_cursor;
9 dbms_sql.parse(:curnum,'begin insert into orablog.customer(fullname,firstname,lastname) values(''TestUser'',''Test'',''User'');end;',dbms_sql.native);
10 end;
11 /
12 print curnum
13 -- -------------------------------------------------------
14 -- end of declaring the "procedure"
15 -- -------------------------------------------------------
16 -- -------------------------------------------------------
17 -- Call the dynamic procedure
18 -- -------------------------------------------------------
19 declare
20 n number;
21 begin
22 n:=dbms_sql.execute(:curnum);
23 end;
24 /
25 -- --------------------------------------------------------
26 -- End of dynamic procedure call
27* -- --------------------------------------------------------
28 .
SQL> @dyn
declare
*
ERROR at line 1:
ORA-29471: DBMS_SQL access denied
ORA-06512: at "SYS.DBMS_SQL", line 1169
ORA-06512: at line 4
CURNUM
----------
declare
*
ERROR at line 1:
ORA-29471: DBMS_SQL access denied
ORA-06512: at "SYS.DBMS_SQL", line 1792
ORA-06512: at line 4
SQL>
不,我们不能通过动态PL/SQL 代码块执行插入操作。ORA-29471 是一个安全问题,因此被阻止。
为用户 VV 关闭只读属性以展示如何撤销:
SQL> sho user
USER is "SYS"
SQL> alter user vv read write;
User altered.
SQL>
在我们使用户 VV 变为可读写之后,再次尝试动态过程:
SQL> connect vv/vv@//192.168.56.18:1521/freepdb1
Connected.
SQL> @dyn
PL/SQL procedure successfully completed.
CURNUM
----------
1166850649
PL/SQL procedure successfully completed.
SQL>
关于用户只读状态的存储。我们需要能够检查数据库并测试哪些用户是只读的:
SQL> desc dba_users
Name Null? Type
----------------------------------------- -------- ----------------------------
USERNAME NOT NULL VARCHAR2(128)
USER_ID NOT NULL NUMBER
PASSWORD VARCHAR2(4000)
ACCOUNT_STATUS NOT NULL VARCHAR2(32)
LOCK_DATE DATE
EXPIRY_DATE DATE
DEFAULT_TABLESPACE NOT NULL VARCHAR2(30)
TEMPORARY_TABLESPACE NOT NULL VARCHAR2(30)
LOCAL_TEMP_TABLESPACE VARCHAR2(30)
CREATED NOT NULL DATE
PROFILE NOT NULL VARCHAR2(128)
INITIAL_RSRC_CONSUMER_GROUP VARCHAR2(128)
EXTERNAL_NAME VARCHAR2(4000)
PASSWORD_VERSIONS VARCHAR2(17)
EDITIONS_ENABLED VARCHAR2(1)
AUTHENTICATION_TYPE VARCHAR2(8)
PROXY_ONLY_CONNECT VARCHAR2(1)
COMMON VARCHAR2(3)
LAST_LOGIN TIMESTAMP(9) WITH TIME ZONE
ORACLE_MAINTAINED VARCHAR2(1)
INHERITED VARCHAR2(3)
DEFAULT_COLLATION VARCHAR2(100)
IMPLICIT VARCHAR2(3)
ALL_SHARD VARCHAR2(3)
EXTERNAL_SHARD VARCHAR2(3)
PASSWORD_CHANGE_DATE DATE
MANDATORY_PROFILE_VIOLATION VARCHAR2(3)
PROTECTED VARCHAR2(3)
READ_ONLY VARCHAR2(3)
DICTIONARY_PROTECTED VARCHAR2(3)
SQL>
现在我们可以检查 READ_ONLY 列,看看数据库中哪些用户是只读的:
SQL> select username from dba_users where read_only='YES';
USERNAME
--------------------------------------------------------------------------------
VX
SQL>
这是正确的,因为我们之前将 VV 设置为可读写,并将 VX 创建为只读。
该标志存储在 SYS.USER$ 的 SPARE1 列中作为一个位标志:
decode(bitand(u.spare1, 524288), 524288, 'YES', 'NO'),
-- READ_ONLY
总结来说,只读允许 SELECT 并阻止 INSERT/UPDATE 和 DELETE,但我们仍然可以执行 EXECUTE,只要 PL/SQL 没有尝试更改数据。我不知道在用户是只读时,“可以/不可以”在 PL/SQL 中执行什么操作。更多的测试将能进一步展示这一点。
这是一个阻止更改的有用特性,但如果攻击者想要从你的数据库中窃取所有个人信息或信用卡信息,那么他不需要更改,他只需要读取!
原文标题:Read Only Users in 23c
原文作者:Pete Finnigan
原文链接:http://www.petefinnigan.com/weblog/archives/00001521.htm




