学习如何在PostgreSQL中管理安全性

 阅读约 24 分钟

学习如何在PostgreSQL中管理安全性


  • 来源 | 愿码(ChainDesk.CN)内容编辑
  • 愿码Slogan | 连接每个程序员的故事
  • 网站 | http://chaindesk.cn
  • 愿码愿景 | 打造全学科IT系统免费课程,助力小白用户、初级工程师0成本免费系统学习、低成本进阶,帮助BAT一线资深工程师成长并利用自身优势创造睡后收入。
  • 官方公众号 | 愿码 | 愿码服务号 | 区块链部落
  • 免费加入愿码全思维工程师社群 | 任一公众号回复“愿码”两个字获取入群二维码

本文阅读时长:17min

在处理安全性时,记住这些级别以便以有组织的方式处理与安全相关的问题。

· 绑定地址:listen_addresses在文件中 postgresql.conf

· 基于主机的访问控制:pg_hba.conf文件

· 实例级权限:用户,角色,数据库创建,登录和复制

· 数据库级权限:连接,创建模式等

· 架构级权限:使用架构并在架构内创建对象

· 表级权限:选择,插入,更新等

· 列级权限:允许或限制对列的访问

· 行级安全性:限制对行的访问

为了读取值, PostgreSQL必须确保我们在每个级别都有足够的权限。整个权限链必须是正确的。

在本文中,您将学习处理SSL,列级安全性和配置默认权限等的过程。

了解绑定地址和连接


配置PostgreSQL服务器时,需要做的第一件事就是定义远程访问。默认情况下,PostgreSQL不接受远程连接。重要的是由于PostgreSQL不能监听端口,所以导致它接收不到连接。如果我们尝试连接,错误消息实际上将来自操作系统。

假设有一个使用默认配置的数据库服务器192.168.0.123,将发生以下情况:

iMac:~ hs$ telnet 192.168.0.123 5432 
Trying 192.168.0.123... 
telnet: connect to address 192.168.0.123: Connection refused 
telnet: Unable to connect to remote host

Telnet尝试在端口上创建连接,5432并立即被远程框拒绝。从外面看,看起来PostgreSQL根本就没有运行。

成功连接的关键可以在postgresql.conf文件中找到:

# - Connection Settings - 

# shop_addresses ='localhost'
# 要监听的IP地址;
# 逗号分隔的地址列表;
# defaults为'localhost'; 使用'*'表示所有
# (更改需要重启)

该listen_addresses设置将告诉PostgreSQL要监听的地址。从技术上讲,这些地址是绑定地址。这究竟意味着什么?假设我们的机器中有四个IP地址。我们这些IP地址只监听其中的三个。PostgreSQL只考虑这三个IP的请求,而不考虑第四个。

如果我们放入 *,PostgreSQL会监听分配给您机器的每个IP。

但是,有更多与连接管理相关的设置对于理解非常重要。它们如下:

#port = 5432 
              # (change requires restart) 
max_connections = 100 
              # (change requires restart) 
# Note: Increasing max_connections costs ~400 bytes of 
# shared memory per 
# connection slot, plus lock space 
# (see max_locks_per_transaction). 
#superuser_reserved_connections = 3 
              # (change requires restart) 
#unix_socket_directories = '/tmp' 
              # comma-separated list of directories 
              # (change requires restart) 
#unix_socket_group = '' 
              # (change requires restart) 
#unix_socket_permissions = 0777 
              # begin with 0 to use octal notation 
              # (change requires restart)

首先,PostgreSQL侦听单个TCP端口,默认值为5432。请记住,PostgreSQL只会侦听单个端口。每当有请求进入时,postmaster将创建一个新进程来处理连接。默认情况下,最多允许100个普通连接。最重要的是,为超级用户保留了三个额外的连接。这意味着我们可以拥有97个连接加上三个超级用户或100个超级用户连接。

处理SSL


PostgreSQL允许我们加密服务器和客户端之间的传输。加密非常有用,特别是如果我们进行远距离通信。SSL提供了一种简单而安全的方式来确保没有人能够收听您的通信。

在本节中,我们将学习如何设置SSL。

首先要做的是在服务器启动时将ssl参数设置on为postgresql.conf文件。在下一步中,我们可以将SSL证书放入$PGDATA目录中。如果我们不希望证书位于其他目录中,请更改以下参数:

#ssl_cert_file = 'server.crt'    # (change requires restart) 
#ssl_key_file = 'server.key'     # (change requires restart) 
#ssl_ca_file = ''                # (change requires restart) 
#ssl_crl_file = ''               # (change requires restart)

如果我们要使用自签名证书,请执行以下步骤:

openssl req -new -text -out server.req

回答OpenSSL提出的问题。确保我们输入本地主机名作为通用名称。我们可以将密码留空。此调用将生成一个受密码保护的密钥; 它不会接受长度少于四个字符的密码短语。

要删除密码(如果要自动启动服务器,则必须如此),请运行以下命令:

openssl rsa -in privkey.pem -out server.key
rm privkey.pem

输入旧密码以解锁现有密钥。现在,执行此操作将证书转换为自签名证书,并将密钥和证书复制到服务器将查找的位置:

openssl req -x509 -in server.req -text
  -key server.key -out server.crt

执行此操作后,请确保文件具有正确的权限集:

chmod og-rwx server.key

一旦将适当的规则放入pg_hba.conf文件中,我们就可以使用SSL连接到您的服务器。要验证我们确实使用SSL,请考虑签出该pg_stat_ssl功能。它将告诉我们每个连接以及它是否使用SSL,它将提供有关加密的一些重要信息:

test=# \d pg_stat_sslView  "pg_catalog.pg_stat_ssl"
Column | Type | Modifiers -------------+----------+----------- pid | integer | ssl | boolean | version | text | cipher | text | bits | integer | compression | boolean |

clientdn     | text     |

如果ssl进程的字段包含true; PostgreSQL做了我们期望它做的事情:

postgres=# select * from pg_stat_ssl; 
-[ RECORD 1 ] 
---------------------------- 
pid         | 20075
ssl         | t 
version     | TLSv1.2
cipher      | ECDHE-RSA-AES256-GCM-SHA384 
bits        | 256
compression | f 
clientdn    |

处理实例级安全性

到目前为止,我们已经配置了绑定地址,我们告诉PostgreSQL使用哪种IP范围进行身份验证。到目前为止,配置纯粹与网络相关。

在下一步中,我们可以将注意力转移到实例级别的权限。最重要的是要知道PostgreSQL中的用户存在于实例级别。如果我们创建一个用户,它不仅在一个数据库中可见; 它可以被所有数据库看到。用户可能只具有访问单个数据库的权限,但基本上用户是在实例级别创建的。

对于那些刚接触PostgreSQL的人来说,还有一件事要记住:用户和角色是一回事。CREATE ROLE和CREATE USER子句有不同的默认值(字面上,唯一的区别是LOGIN默认情况下角色没有得到属性),但在一天结束时,用户和角色是相同的。因此,CREATE ROLE和CREATE USER子句支持完全相同的语法:

test=# \h CREATE USER
Command: CREATE USER
Description: define a new database role
Syntax:
CREATE USER name [ [ WITH ] option [ ... ] ]
where option can be:

SUPERUSER | NOSUPERUSER
| CREATEDB | NOCREATEDB
| CREATEROLE | NOCREATEROLE
| INHERIT | NOINHERIT
| LOGIN | NOLOGIN
| REPLICATION | NOREPLICATION
| BYPASSRLS | NOBYPASSRLS
| CONNECTION LIMIT connlimit
| [ ENCRYPTED ] PASSWORD 'password'
| VALID UNTIL 'timestamp'
| IN ROLE role_name [, ...]
| IN GROUP role_name [, ...]
| ROLE role_name [, ...]
| ADMIN role_name [, ...]
| USER role_name [, ...]
| SYSID uid

让我们逐个讨论这些语法元素。我们首先看到的是用户可以是超级用户或普通用户。如果某人被标记为a SUPERUSER ,则不再有普通用户必须面对的任何限制。A SUPERUSER 可以根据需要删除对象(数据库等)。

下一个重要的事情是它在实例级别上获取创建新数据库的权限。

规则是这样的:创建者总是自动成为对象的所有者(除非另有说明,否则可以使用该CREATE DATABASE子句)。美丽的是,对象所有者也可以再次丢弃一个对象。

下一个重要的是INHERIT/ NOINHERITclause。如果INHERIT设置了子句(这是默认值),则用户可以继承其他用户的权限。使用继承权限允许我们使用角色,这是抽象权限的好方法。例如,我们可以创建角色bookkeeper并使许多其他角色继承bookkeeper。我们的想法是bookkeeper,即使我们有很多人从事会计工作,我们也只需要告诉PostgreSQL一次允许做什么。

该LOGIN/ NOLOGIN子句定义一个角色是否可以登录到该实例。

在理论介绍之后,是时候实际创建用户并看看在实际示例中如何使用事物:

test=# CREATE ROLE  bookkeeper NOLOGIN;  
CREATE ROLE 
test=# CREATE ROLE  joe LOGIN;  
CREATE ROLE 
test=# GRANT  bookkeeper TO joe;  
GRANT ROLE

这里做的第一件事bookkeeper是创建一个名为的角色。

请注意,我们不希望人们以身份登录bookkeeper,因此角色标记为NOLOGIN。

另请注意,NOLOGIN如果使用该CREATE ROLE子句,则为默认值。如果您更喜欢该CREATE USER子句,则默认设置为LOGIN。

然后,joe创建角色并标记为LOGIN。最后,将bookkeeper角色分配给joe角色,以便他可以执行bookkeeper实际允许的所有操作。

一旦用户到位,我们可以测试到目前为止我们拥有的内容:

[hs@zenbook ~]$ psql test -U bookkeeper 
psql: FATAL:  role "bookkeeper" is not permitted to log in

正如所料,该bookkeeper角色不允许登录系统。如果joe角色尝试登录会发生什么?

    [hs@zenbook ~]$ psql test -U joe
    ... 
    test=>

这实际上将按预期工作。但请注意,命令提示符已更改。这只是PostgreSQL向您显示您未以超级用户身份登录的一种方式。

创建用户后,可能需要对其进行修改。我们可能想要改变的一件事是密码。在PostgreSQL中,允许用户更改自己的密码。下面是它的工作原理:

test=> ALTER  ROLE  joe PASSWORD 'abc';  
ALTER  ROLE 
test=> SELECT current_user; 
 current_user 
--------------  
 joe 
(1 row)

该ALTER ROLE条款(或ALTER USER)将允许我们改变它可以创建用户时设置大多数设置。但是,管理用户还有很多。在许多情况下,我们希望为用户分配特殊参数。该ALTER USER条款为我们提供了这样做的方法:

ALTER ROLE { role_specification | ALL } 
     [ IN DATABASE database_name ] 
              SET configuration_parameter { TO | = } { value | DEFAULT } 
ALTER ROLE { role_specification | ALL } 
     [ IN DATABASE database_name ] 
              SET configuration_parameter FROM CURRENT 
ALTER ROLE { role_specification | ALL } 
     [ IN DATABASE database_name ] RESET configuration_parameter 
ALTER ROLE { role_specification | ALL } 
     [ IN DATABASE database_name ] RESET ALL

语法非常简单,非常简单。为了描述为什么这非常有用,我添加了一个真实的例子。让我们假设Joe碰巧住在毛里求斯岛上。当他登录时,即使他的数据库服务器位于欧洲,他也希望自己在他自己的时区:

test=> ALTER  ROLE  joe SET TimeZone = 'UTC-4';  
ALTER  ROLE 
test=> SELECT now(); 
             now 
------------------------------- 
2017-01-09 20:36:48.571584+01 
(1 row) 
 
test=> q 
[hs@zenbook ~]$ psql  test  -U joe 
... 
test=> SELECT now(); 
           now 
------------------------------- 
2017-01-09 23:36:53.357845+04 
(1 row)

该ALTER ROLE子句将修改用户。一旦joe重新连接,就会为他设置时区。

时区不会立即更改。您应该重新连接或使用SET ... TO DEFAULT子句。

这里重要的是这对于某些内存参数也是可能的,例如work_mem等等。

数据库级别的安全性


在实例级别配置用户之后,可以深入挖掘并查看在数据库级别可以执行的操作。出现的第一个主要问题是:我们明确允许Joe登录数据库实例,但是谁或什么允许Joe实际连接到其中一个数据库?也许我们不希望Joe访问系统中的所有数据库。限制对某些数据库的访问正是我们在此级别上可以实现的目标。

对于数据库,可以使用GRANT子句设置以下权限:

GRANT { { CREATE | CONNECT | TEMPORARY | TEMP } [, ...] 
     | ALL [ PRIVILEGES ] } 
    ON DATABASE database_name [, ...] 
    TO role_specification [, ...] [ WITH GRANT OPTION ]  

数据库级别有两个主要权限值得密切关注:

· CREATE:这允许某人在数据库中创建模式。请注意,CREATE子句不允许创建表; 它是关于模式的。在PostgreSQL中,表位于模式中,因此您必须首先进入模式级别才能创建表。

· CONNECT:这允许有人连接到数据库。

现在的问题是:没有人明确CONNECT为joe角色分配权限,那么这些权限实际上来自何处?答案是:有一个叫做的东西public,类似于Unix世界。如果世界被允许做某事,那么joe,谁是一般公众的一部分。

最重要的是,public它不是一个角色,它可以被删除和重命名。我们可以简单地将其视为系统中每个人的等价物。

因此,为了确保不是每个人都可以随时连接到任何数据库,CONNECT可能必须从公众中撤销。为此,我们可以以超级用户身份进行连接并解决问题:

[hs@zenbook ~]$ psql  test  -U postgres 
... 
test=# REVOKE ALL ON DATABASE test FROM public;  
REVOKE 
test=# \q 
[hs@zenbook ~]$ psql test -U joe 
psql:  FATAL:  permission denied for database "test"  
DETAIL:  User does not have CONNECT privilege.

我们可以看到,该joe角色不再允许连接。此时,只有超级用户才能进行测试。

通常,postgres 即使在创建其他数据库之前,最好还是从数据库中撤消权限。这个概念背后的想法是,这些权限将不再存在于所有新创建的数据库中。如果某人需要访问某个数据库,则必须明确授予权限。权利不再自动存在。

如果我们想允许joe角色连接到测试数据库,请以超级用户身份尝试以下行:

[hs@zenbook ~]$ psql test -U postgres 
... 
test=# GRANT CONNECT ON DATABASE test TO bookkeeper;  
GRANT 
test=# \q 
[hs@zenbook ~]$ psql test -U joe 
...  
test=>

基本上,这里有两种选择:

· 我们可以joe直接允许角色,以便只有joe角色才能连接。

· 或者,我们可以为该bookkeeper角色授予权限。请记住,joe角色将继承角色的所有权限bookkeeper,因此,如果我们希望所有会计师都能够连接到数据库,则为角色分配权限bookkeeper似乎是一个有吸引力的想法。

如果我们为该bookkeeper角色授予权限,则它没有风险,因为该角色不允许首先登录到该实例,因此它纯粹作为权限来源。

处理列级安全性


在某些情况下,并不是每个人都可以看到所有数据。想象一下银行,有些人可能会看到有关银行帐户的全部信息,而其他人可能仅限于数据的一部分。在现实世界的情况下,可能不允许某人阅读余额栏,或者有人可能看不到人民贷款的利率。

另一个例子是,人们可以看到人们的个人资料,但不能看到他们的照片或其他私人信息。现在的问题是:如何使用列级安全性?

为了证明这一点,我们将向属于该joe角色的现有表添加一列:

test=> ALTER TABLE t_useful ADD COLUMN name text;  
ALTER TABLE

该表现在由两列组成。该示例的目标是确保用户只能看到其中一列:

test=> \d t_useful 
    Table  "public.t_useful"  
 Column |   Type   | Modifiers 
--------+---------+-----------  
 id     | integer  | 
 name   | text     |

作为超级用户,让我们创建一个用户并让它访问包含我们表的模式:

test=# CREATE ROLE paul LOGIN;  
CREATE ROLE 
test=# GRANT CONNECT ON DATABASE test TO paul;  
GRANT 
test=# GRANT USAGE ON SCHEMA public TO paul;  
GRANT

CONNECT被撤销了public。因此,明确授予绝对是必要的,以确保我们甚至可以到达桌面。

该SELECT权限可以赋予paul角色:

test=# GRANT  SELECT (id)  ON t_useful TO paul;  
GRANT 

基本上,这已经足够了。已经可以以用户身份连接到数据库paul并阅读该列:

[hs@zenbook ~]$ psql test -U paul 
... 
test=> SELECT id FROM t_useful; 
id 
---- 
(0 rows)

如果我们使用列级权限,请记住一件重要的事情,我们应该停止使用SELECT *,因为它不再起作用了:

test=> SELECT * FROM t_useful; 
ERROR:  permission denied for relation t_useful
  • 仍然意味着所有列,但由于无法访问所有列,事情将立即出错。

配置默认权限


到目前为止,已经配置了很多东西。如果将新表添加到系统会发生什么?逐个处理这些表并设置适当的权限可能会非常痛苦和风险。如果这些事情会自动发生,那不是很好吗?这正是该ALTER DEFAULT PRIVILEGES条款的作用。这个想法是为用户提供一个选项,让PostgreSQL在对象出现后立即自动设置所需的权限。有人不能再忘记设置这些权利了。

以下清单显示了语法规范的第一部分:

postgres=# \h ALTER DEFAULT PRIVILEGES
Command: ALTER DEFAULT PRIVILEGES
Description: define default access privileges
Syntax:
ALTER DEFAULT PRIVILEGES
    [ FOR { ROLE | USER } target_role [, ...] ]
    [ IN SCHEMA schema_name [, ...] ]
    abbreviated_grant_or_revoke
where abbreviated_grant_or_revoke is one of:

GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
[, ...] | ALL [ PRIVILEGES ] }
ON TABLES
TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ]
...

基本上,语法与GRANT子句类似,因此使用起来简单直观。为了向我们展示它的工作原理,我编写了一个简单的例子。我们的想法是,如果joe角色创建了一个表,该paul角色将自动使用它:

test=# ALTER DEFAULT PRIVILEGES FOR ROLE joe 
      IN SCHEMA public GRANT ALL ON TABLES TO paul;  
ALTER DEFAULT PRIVILEGES

让我们joe现在作为角色连接并创建一个表:

[hs@zenbook ~]$ psql  test  -U joe 
... 
test=> CREATE TABLE  t_user (id serial, name  text,  passwd text);  
CREATE TABLE

作为paul角色连接将证明该表已分配给适当的权限集:

[hs@zenbook ~]$ psql test -U paul 
... 
test=> SELECT * FROM  t_user; 
 id | name | passwd 
----+------+--------  
(0 row
阅读 276发布于 4月11日
推荐阅读

IT界逻辑思维:每晚十点原创技术{全栈部落}官方公众号定时推送,每周一早上10点推送春哥原创视频教程。

43 人关注
77 篇文章
专栏主页
目录