ZetCode

Derby 安全性

最后修改于 2020 年 7 月 6 日

在下一章中,我们将提及 Derby 的安全选项。

在本章中,我们将简要提及两个基本的安全概念。 用户身份验证和用户授权。 用户身份验证是在允许访问 Derby 系统之前验证用户凭据。 用户授权是一种赋予读取和/或写入 Derby 数据库的权限的方式。

此外,Derby 允许加密存储在磁盘上的数据库文件。 Derby 网络流量可以使用 SSL/TLS 加密协议进行加密。

Derby 默认值

默认情况下,Derby 不需要用户身份验证。 用户名成为程序中的默认模式,并且用户密码被忽略。 要启用身份验证,我们必须修改 Derby 属性。 用户授权已关闭。 此外,Derby 没有数据库超级用户。

数据库所有者

数据库所有者是创建数据库的用户。 如果在没有提供用户的情况下创建数据库,则数据库所有者将设置为默认授权标识符 APP。 当我们启用 SQL 授权时,控制数据库所有者很重要。

数据库加密

Derby 提供了一种加密磁盘上数据的方法。 启动数据库的用户必须提供启动密码。 数据库可以在创建时进行加密。 也可以加密现有的未加密数据库。 当我们加密数据库时,我们还必须指定一个启动密码,这是一个用于生成加密密钥的字母数字字符串。

ij> CONNECT 'jdbc:derby:testdb;create=true;dataEncryption=true;
bootPassword=3344kkllqq**';

我们可以在创建数据库时对其进行加密。 我们将 dataEncryption 属性设置为 true 并提供启动密码。 现在,每次启动数据库时,我们都必须提供启动密码。

ij> CONNECT 'jdbc:derby:testdb';
ERROR XJ040: Failed to start database 'testdb' with class loader 
sun.misc.Launcher$AppClassLoader@360be0, see the next exception for details.
ERROR XBM06: Startup failed. An encrypted database cannot be accessed without 
the correct boot password.

在嵌入模式下,当我们连接到数据库时,我们也会启动它。 当我们尝试在没有启动密码的情况下连接到加密数据库时,Derby 会显示上述错误消息。

ij> CONNECT 'jdbc:derby:testdb;bootPassword=3344kkllqq**';
ij> SHOW CONNECTIONS;
CONNECTION0* -  jdbc:derby:testdb
* = current connection

使用正确的启动密码,我们已成功连接到 testdb 数据库。

身份验证

身份验证是将访问限制给适当的用户。 默认情况下,Derby 中关闭了身份验证。

Derby 有三种提供身份验证的方法。

官方 Derby 文档警告说,Derby 的内置身份验证机制仅适用于开发和测试目的。 强烈建议生产系统依赖于 LDAP 或用户定义的类进行身份验证。

嵌入式

可以在两个级别设置身份验证。 在系统级别或数据库级别。

ij> CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.user.user12', '34klq*');
0 rows inserted/updated/deleted
ij> CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.connection.requireAuthentication', 
'true');
0 rows inserted/updated/deleted

以上两个语句为当前连接的数据库启用了数据库级别的用户身份验证。 我们创建了一个带有密码的用户,并启用了 derby.connection.requireAuthentication 属性。

ij> CONNECT 'jdbc:derby:testdb';
ERROR 08004: Connection authentication failure occurred.  Reason: Invalid authentication..
ij> CONNECT 'jdbc:derby:testdb;user=user12;password=34klq*';
ij> SHOW CONNECTIONS;
CONNECTION0* -  jdbc:derby:testdb
* = current connection

启用用户身份验证后,当我们想要连接到 testdb 数据库时,我们必须提供用户凭据。

客户端/服务器

在接下来的示例中,我们将使用客户端/服务器模式下的 Derby。 我们有一个加密的 testdb 数据库。

$ startNetworkServer &

Derby 服务器已启动。

ij> CONNECT 'jdbc:derby://:1527/dbs/testdb;bootPassword=3344kkllqq**';

当我们第一次连接到 testdb 数据库时,我们必须提供启动密码。 这是因为之前我们加密了 testdb 数据库。

ij> CONNECT 'jdbc:derby://:1527/dbs/testdb';
ij> SHOW CONNECTIONS;
CONNECTION0* -  jdbc:derby://:1527/dbs/testdb
* = current connection

在客户端/服务器模式下,一旦数据库已经启动,我们就不需要启动它了。 与嵌入模式不同,在嵌入模式下,每次我们连接到数据库时,我们也会启动它。

在下一步中,我们将在客户端/服务器模式下启用用户身份验证。 为此,我们需要编辑 derby.properties 文件。

$ stopNetworkServer

首先,如果 Derby 服务器正在运行,则停止它。 请注意,在启用用户身份验证后,我们需要提供用户凭据才能停止服务器。 stopNetworkServer 脚本接受 -user-password 选项。

$ cat dbs/derby.properties 
derby.connection.requireAuthentication=true
derby.user.user12=34klq*
derby.authentication.provider=BUILTIN

在 Derby 系统目录中,我们修改 derby.properties 文件。 如果该文件不存在,则创建它。 在属性文件中,我们启用身份验证并创建一个带有密码的用户。 我们还将身份验证提供程序设置为 Derby BUILTIN。

$ startNetworkServer &

我们启动 Derby 服务器。

$ java  -Dderby.system.home=/home/janbodnar/programming/derby/dbs \
-Dij.protocol=jdbc:derby: -jar $DERBY_HOME/lib/derbyrun.jar ij
ij version 10.8
ij> 

我们启动 ij 工具。

ij> CONNECT 'jdbc:derby:testdb;bootPassword=3344kkllqq**';
ERROR 08004: Connection authentication failure occurred.  Reason: Invalid authentication..

我们尝试连接到 testdb 数据库。 由于 Derby 服务器已重新启动,因此我们提供启动密码。 但是,我们看到一条错误消息。 这是因为我们启用了用户身份验证。 我们还必须提供用户凭据。

ij> CONNECT 'jdbc:derby:testdb;user=user12;password=34klq*;
bootPassword=3344kkllqq**';

使用此连接字符串,我们已成功连接到 testdb 数据库。

用户授权

Derby 中的用户授权允许授予和撤销访问系统、数据库、对象或 SQL 操作的权限。 我们可以在 Derby 中将用户授权属性设置为系统级属性或数据库级属性。

Derby 有几个会影响用户授权的属性。 derby.database.defaultConnectionMode 属性控制默认访问模式。 如果未设置该属性,则该属性默认为 fullAccess,即读写访问。 另外两个选项是 noAccessreadOnlyAccess。 使用 derby.database.fullAccessUsersderby.database.readOnlyAccessUsers,我们控制哪些用户可以对数据库具有读写访问权限,哪些用户可以具有只读访问权限。 derby.database.sqlAuthorization 属性启用 SQL 标准授权。 当 derby.database.sqlAuthorization 属性设置为 true 时,对象所有者可以使用 GRANTREVOKE SQL 语句来设置特定数据库对象或特定 SQL 操作的用户权限。

我们可以授予或撤销的权限包括:DELETEEXECUTEINSERTSELECTREFERENCESTRIGGERUPDATE

derby.database.defaultConnectionMode 属性指定的访问模式会覆盖数据库对象所有者授予的权限。

$ cat dbs/derby.properties 
derby.connection.requireAuthentication=true
derby.user.user12=34klq*
derby.user.user13=33kl33
derby.user.user14=14kl14
derby.user.user15=35rr++
derby.authentication.provider=BUILTIN
derby.database.defaultConnectionMode=readOnlyAccess
derby.database.fullAccessUsers=user12

我们修改 derby.properties 文件。 我们添加了三个用户。 一个用户,user12 具有对数据库的完全访问权限。 另外三个具有默认的只读访问权限。

export DERBY_OPTS=-Dderby.system.home=/home/janbodnar/programming/derby/dbs

请注意,为了让网络服务器知道带有 derby.property 的系统目录在哪里,我们已将 DERBY_OPTS 变量设置为包含 Derby 系统目录。

$ stopNetworkServer
$ startNetworkServer &
$ java  -Dderby.system.home=/home/janbodnar/programming/derby/dbs \
-Dij.protocol=jdbc:derby: -jar $DERBY_HOME/lib/derbyrun.jar ij

我们重新启动网络服务器并启动 ij 工具。

ij> CONNECT 'jdbc:derby:///testdb;user=user13;
password=33kl33;bootPassword=3344kkllqq**';

我们使用 user13 用户连接到 testdb 数据库。 由于我们是第一次连接到数据库,我们也会启动它。 因此,我们需要启动密码,因为数据库之前已加密。

ij> SELECT * FROM USER12.CARS;
ID         |NAME                          |PRICE      
------------------------------------------------------
1          |Audi                          |52642      
2          |Mercedes                      |57127      
3          |Skoda                         |9000       
4          |Volvo                         |29000      
5          |Bentley                       |350000     
6          |Citroen                       |21000      
7          |Hummer                        |41400      
8          |Volkswagen                    |21600      

8 rows selected

user13 具有查看位于 USER12 模式中的 CARS 表中的数据的权限。

ij> INSERT INTO USER12.CARS VALUES(9, 'Toyota', 27000);
ERROR 25502: An SQL data change is not permitted for a read-only connection, 
user or database.

但是,尝试修改 CARS 表中的数据会导致错误。 未授予执行更改的权限。

ij> DISCONNECT;
ij> CONNECT 'jdbc:derby:///testdb;user=user12;
password=34klq*';

我们关闭连接并以 user12 的身份连接。 此用户在属性文件中被授予了完全访问权限。 即使 user12 是数据库的所有者和 CARS 表的所有者,除非通过 Derby 属性授予完全访问权限,否则他无法修改该表。

ij> INSERT INTO CARS VALUES(9, 'Toyota', 27000);
1 row inserted/updated/deleted
ij> SELECT * FROM CARS WHERE ID = 9;
ID         |NAME                          |PRICE      
------------------------------------------------------
9          |Toyota                        |27000      

1 row selected

我们已成功向 CARS 表中添加了一个新行。

SQL 授权

数据库或对象(如表)的所有者可以进一步限制使用数据库对象的权限。 我们可以使用 GRANTREVOKE 语句来授予或撤销权限。 数据库和表的所有者是创建它们的当前用户。 请注意,derby.database.defaultConnectionMode 覆盖了 GRANT 语句给出的权限。 因此,如果用户通过默认连接模式获得了 readOnlyAccess,则即使他通过 GRANT 语句获得了权限,他也无法修改数据库对象。

derby.database.sqlAuthorization 属性设置为 true 时,对象所有者可以使用 GRANTREVOKE SQL 语句来设置特定数据库对象或特定 SQL 操作的用户权限。 请注意,在 derby.properties 文件中设置系统范围的属性 仅对新数据库有效。 对于现有数据库,我们只能设置数据库范围的 derby.database.sqlAuthorization 属性。 在我们将 derby.database.sqlAuthorization 属性设置为 true 后,我们无法将该属性设置回 false。

ij> CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.database.sqlAuthorization', 
'true');

derby.database.sqlAuthorization 属性已设置为 true。 该属性是静态的。 我们必须重新启动 testdb 数据库才能使该属性生效。

ij> CONNECT 'jdbc:derby:///testdb;shutdown=true;
user=user12;password=34klq*';

ij> CONNECT 'jdbc:derby:///testdb;user=user12;
password=34klq*;bootPassword=3344kkllqq**';

我们关闭 testdb 数据库并再次启动它。

ij(CONNECTION1)> GRANT SELECT ON CARS TO user15;
0 rows inserted/updated/deleted

我们向表 CARS 上的 user15 提供 SELECT 权限。

ij(CONNECTION1)> UPDATE CARS SET PRICE=27001 WHERE ID=9;
1 row inserted/updated/deleted

ij(CONNECTION1)> SELECT * FROM CARS;
ID         |NAME                          |PRICE      
------------------------------------------------------
1          |Audi                          |52642      
2          |Mercedes                      |57127      
3          |Skoda                         |9000       
4          |Volvo                         |29000      
5          |Bentley                       |350000     
6          |Citroen                       |21000      
7          |Hummer                        |41400      
8          |Volkswagen                    |21600      
9          |Toyota                        |27001 

作为表所有者的 user12 具有完全权限。 上述命令确认他具有对 CARS 表的 UPDATESELECT 权限。

ij(CONNECTION1)> DISCONNECT;
ij> CONNECT 'jdbc:derby:///testdb;user=user14;
password=14kl14';
ij(CONNECTION1)> SELECT * FROM USER12.CARS;
ERROR 42502: User 'USER14' does not have SELECT permission 
on column 'ID' of table 'USER12'.'CARS'.

我们断开与数据库的连接并以 user14 身份连接。 尝试执行 SELECT 语句会导致错误。 user14 没有从 CARS 表中 SELECT 数据的权限。

ij(CONNECTION1)> DISCONNECT;
ij> CONNECT 'jdbc:derby:///testdb;user=user15;
password=35rr++';

ij> SELECT * FROM USER12.CARS;
ID         |NAME                          |PRICE      
------------------------------------------------------
1          |Audi                          |52642      
2          |Mercedes                      |57127      
3          |Skoda                         |9000       
4          |Volvo                         |29000      
5          |Bentley                       |350000     
6          |Citroen                       |21000      
7          |Hummer                        |41400      
8          |Volkswagen                    |21600   
9          |Toyota                        |27000      

8 rows selected

接下来,我们以 user15 身份连接。 用户可以从 CARS 表中选择数据。

ij(CONNECTION1)> SELECT * FROM USER12.AUTHORS;
ERROR 42502: User 'USER15' does not have SELECT 
permission on column 'ID' of table 'USER12'.'AUTHORS'.

但是他不能从 AUTHORS 表中选择数据。 表所有者 user12 未授予选择该表数据的权限。

ij(CONNECTION1)> UPDATE USER12.CARS SET PRICE=27000 WHERE ID=9;
ERROR 25502: An SQL data change is not permitted for a read-only 
connection, user or database.

user15 也没有对 CARS 表的 UPDATE 权限。

在本章中,我们讨论了 Derby 中的安全选项。