强化 PostgreSQL 变得越来越重要。如今,安全为王,人们想知道如何使 PostgreSQL 安全。我们中的一些人可能还记得近年来MongoDB 发生的事情,我们当然希望避免 PostgreSQL 世界中出现类似的安全问题。MongoDB 发生的事情实际上令人震惊:由于默认安全设置不佳,数千个数据库被勒索赎金——这绝对是一场噩梦,不仅极大地损害了 MongoDB,而且还极大地损害了整个行业的声誉。PostgreSQL 人员尽其所能避免在我们的生态系统中重复这种表现。
当有人谈论您的公司时,#ransomware 标签不是您想看到的。为了避免一些最常见的问题,我们编制了“PostgreSQL 安全问题的最佳选择”,可用作改进设置的12 步指南。
1.避免宽松的listen_addresses设置
PostgreSQL 作为服务器进程运行,人们想要连接到数据库。问题是:这些联系来自哪里?listen_addresses可以在中找到的设置postgresql.conf控制这些绑定地址。
换句话说:如果listen_addresses设置为 ‘*’,PostgreSQL 将侦听所有网络设备,考虑这些连接并进入下一阶段,即评估pg_hba.conf. 在所有设备上收听是一个问题,因为不良行为者很容易向您发送身份验证请求——灾难就在pg_hba.conf咫尺之遥。
推荐:
- 如果只需要本地连接:
放 listen_addresses = ‘localhost’ - 如果需要远程连接:
放 listen_addresses = ‘localhost,’
如果你根本不听,你肯定更安全。如果您已经限制网络访问,PostgreSQL 甚至不必拒绝您的连接。
2. 在 pg_hba.conf 中使用“信任”
处理完listen_addresses(= 绑定地址) 后,PostgreSQL 将处理pg_hba.conf以确定是否实际允许连接。出现的主要问题是:谈论时可能会出现什么问题pg_hba.conf?嗯,有些事情值得一提。
我们经常看到的是,人们使用“信任”来确保人们可以在没有密码的情况下连接到 PostgreSQL。trust如果您正在努力强化 PostgreSQL,那么使用基本上是您能做的最糟糕的事情。对于本地连接,“peer”可能是一个有效的选择——信任肯定不是。
推荐:
- trust如果您正在强化 PostgreSQL,请不要使用(尤其不要用于远程连接)。
- 改用 scram-sha-256 和 SSL
- 如果可能,请避免允许来自任何地方的连接(网络掩码 0.0.0.0/0)的条目。相反,限制对那些真正需要访问的主机和子网的访问。
- 不要创建pg_hba.conf允许连接到所有数据库或所有用户的条目。请明确点。
- 将文档添加到文件中的每一行,以便您了解为什么需要访问权限、谁请求访问权限以及在需要时与谁联系。否则,您几乎没有机会清理和删除不再需要的条目。此外,如果需要更改密码,您知道该联系谁。
3. 摆脱 PostgreSQL 中的 md5 密码
多年来一直md5是在 PostgreSQL 中进行密码认证的首选方法。然而,这样的日子md5早已一去不复返了。您甚至可以从 Internet 下载包含最常用哈希的现成文件,并更快地破解密码。
换句话说:忘记md5并转向更强的哈希。如果您想了解如何迁移md5以scram-sha-256确保您查看我们的帖子“从 MD5 到 PostgreSQL 中的 scram-sha-256”。
推荐:
- 将 md5 移动到 scram-sha-256
4. 处理架构和数据库的 PUBLIC 权限
在 PostgreSQL 中有一个东西叫做PUBLIC. 它基本上相当于“UNIX 世界”的数据库。正如您将看到的,它可能会导致一些问题——但是,这是可以避免的。
以下是通常会发生的情况:
postgres=# CREATE USER joe;
CREATE ROLE
postgres=# \c postgres joe
You are now connected to database "postgres" as user "joe".
postgres=> SELECT current_user;
current_user
--------------
joe
(1 row)
我们已经创建了一个用户并以joe. 我们在这里看到的是joe不允许创建新数据库,这正是我们所期望的。BUT:joe允许连接到一些我们目前甚至没有听说过的其他数据库。幸运的是,joe不允许读取此数据库中的任何对象:
postgres=> CREATE DATABASE joedb;
ERROR: permission denied to create database
postgres=> \c demo
You are now connected to database "demo" as user "joe".
demo=> SELECT * FROM t_demo;
ERROR: permission denied for table t_demo
但是,joe默认情况下允许创建表(公共模式),这当然不是一个好主意:
demo=> CREATE TABLE bad_idea (id int);
CREATE TABLE
因此,我们确定了两个关键问题:
- joe 允许连接到其他数据库
- joe 允许向公共模式发送垃圾邮件
这比乍一看更糟糕。经验表明,多年来在 PostgreSQL 中发现的大多数提权攻击向量都是通过在数据库中创建恶意对象来实现的。
所以,我们必须修复它:
demo=# REVOKE ALL ON DATABASE demo FROM public;
REVOKE
demo=# REVOKE ALL ON SCHEMA public FROM public;
REVOKE
现在我们已经完成了,我们可以以“joe”的身份重新连接到演示数据库。在 psql 中,可以使用 \c 命令来做到这一点。如果您使用图形用户界面,只需更改您的数据库连接并以用户 joe 登录:
demo=# \c demo joe FATAL: permission denied for database "demo" DETAIL: User does not have CONNECT privilege. Previous connection kept
首先,我们已经撤销了权限,PUBLIC以确保数据库连接不再可能。然后我们对公共架构有固定的权限。如您所见,连接不再可能。
建议:
- PUBLIC从您的数据库权限中删除
- PUBLIC从您的公共架构权限中删除
TEMP从不需要它的所有用户那里撤销对数据库的特权也可能是一个好主意。一些特权提升攻击也适用于临时对象。
此外,请确保正确测试您的设置以确保没有泄漏。
5. 避免 ALTER USER … SET PASSWORD …
在 PostgreSQL 中更改密码很容易。大多数人习惯ALTER USER … SET PASSWORD这样做。但是,有一个问题:这条 SQL 语句最终会以 PLAIN 文本形式出现在您的数据库日志中,这当然是一个主要问题。
推荐:
- 使用您信任的 PostgreSQL 客户端工具来更改密码而不是ALTER ROLE. 例如,psql\password和 pgAdmin 有一个“更改密码”对话框。
这个建议似乎很荒谬。然而,这背后有一些原因。要更改密码,有协议支持可以绕过日志中的纯文本密码问题。通过直观地做事——而不是通过简单的 SQL——大多数 GUI 会为你解决问题。
提示: CYBERTEC PostgreSQL Enterprise Edition (PGEE)甚至ALTER USER … SET PASSWORD明确禁止在日志流中避免风险密码。
6. 使用 ALTER DEFAULT PRIVILEGES
假设您正在运行一个应用程序。您的数据库包含 200 个表。为无数用户设置了完美的权限。让我们假设我们要更新此应用程序,以便执行 DDL 来进行更改。但是如果有人犯了错误怎么办?如果权限设置不正确怎么办?小问题会开始累积。
这个问题的解决方案是ALTER DEFAULT PRIVILEGES:
demo=# \h ALTER DEFAULT
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 ]
…
这个想法是在创建对象之前很久就定义默认权限。每当您创建数据库对象时,默认权限都会自动启动并为您修复问题。在这种情况下,PostgreSQL 将通过自动设置新对象的权限,大大简化您的强化过程。
7. 使用 SSL
SSL 是 PostgreSQL 安全领域中最重要的主题之一。如果您想强化 PostgreSQL 数据库,则无法绕过 SSL。
PostgreSQL 提供各种级别的 SSL,并允许您加密客户端和服务器之间的连接。通常,我们建议至少使用TLS 1.2以确保足够高的安全级别。
如果您想了解有关 SSL 的更多信息,并想知道如何设置它,请查看我们的页面。
8. 安全地编写 SECURITY DEFINER 函数
存储过程和服务器端功能通常是一个主要的安全问题。PostgreSQL 中有两种执行函数的方式:
- 以“你是谁”的身份执行一个功能
- 以代码作者身份执行函数
默认情况下,函数以当前用户身份执行。换句话说:如果您当前是用户,joe该函数将作为joe. 但是,有时以函数的作者身份运行代码并因此使用不同的安全设置会很有用。这样,您可以让具有低权限的用户以受控方式执行某些需要提升权限的操作。这样做的方法是SECURITY DEFINER在创建函数时使用该选项。
但是强大的工具也很危险,因此您必须仔细定义这些功能。有关详细信息,请阅读我们关于SECURITY DEFINER 函数的文章。
9.避免数据库函数中的SQL注入
SQL 注入不仅是应用程序(客户端)端的问题,它还会影响数据库中的过程代码。如果您想避免 SQL 注入,我们建议您继续阅读并了解更多信息。
考虑这个愚蠢的函数:
CREATE FUNCTION tally(table_name text) RETURNS bigint
LANGUAGE plpgsql AS
$$DECLARE
result bigint;
BEGIN
EXECUTE 'SELECT count(*) FROM ' || table_name
INTO result;
RETURN result;
END;$$;
现在,任何可以控制提供给函数的参数的攻击者都可以发起拒绝服务攻击:
SELECT tally('generate_series(1, 100000000000000000000)');
或者他们可以找出您帐户上的金额:
SELECT tally('generate_series(1, 1000000)
UNION
SELECT amount::bigint FROM account WHERE name = ''loser''');
使用通常的安全预防措施:
- 仅在必要时使用动态 SQL。
- 仅在必要时将字符串数据类型用于参数。
- 始终用于format()构造 SQL 查询字符串。
10. 尽可能限制超级用户访问
如果对安全关键系统具有管理访问权限的人数尽可能少,那总是好的。您想要走多远取决于您的安全需求:
- 加固数据库机器,以便除数据库管理员之外的任何人都无法获得对机器的 shell 访问权限。任何作为 PostgreSQL 操作系统用户具有 shell 访问权限的人都可以完全控制数据库。
- 为管理员使用个性化的超级用户帐户,以便您可以在需要时快速撤销对管理员的访问权限。
- 仅在真正需要的地方使用超级用户。例如,复制连接或备份不需要超级用户。
- 限制超级用户帐户的访问pg_hba.conf。理想情况下,只允许本地连接。如果您不想这样做,请限制对管理员个人系统的访问。
- 对于非常高的安全要求,请确保没有人知道超级用户密码,并将其放在至少只能由两个人打开的保险箱中。每次使用后更改密码。
一般来说,使用超级用户是危险的。
推荐:
- 永远不要以超级用户身份运行应用程序
- 尽可能限制超级用户
11. 定期更新你的 PostgreSQL 数据库
最后,定期更新 PostgreSQL 很重要。请记住,大多数次要版本更新(例如 13.0 -> 13.2 等)都带有与安全相关的更新,这些更新对于减少系统的攻击面至关重要。
PostgreSQL 安全更新会定期提供,我们建议您尽快应用它们。
12. 加密你的整个服务器:PostgreSQL TDE
到目前为止,您已经学习了如何加密客户端和服务器之间的连接。但是,有时需要对包括存储在内的整个服务器进行加密。PostgreSQL TDE 正是这样做的:

要了解更多信息,请查看我们关于PostgreSQL TDE的网站。我们提供完全加密的堆栈来帮助您实现最大的 PostgreSQL 安全性。TDE 可免费使用(开源)。




