暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

EDB-如何保障 Postgres 的安全

新智锦绣 2025-07-18
196

点击蓝字关注我们



保障数据安全对于任何企业的成功以及客户的安全至关重要。本文想尽量提供一个全面的概述,以检查PostgreSQL部署的安全性。

本文的大部分讨论将适用于PostgreSQL和EDB Postgres Advanced Server(EPAS),不过也会涉及一些仅在EPAS中可用的功能。

在审查保障Postgres安全的组件和流程时,通常将查看以下部分内容:

01

访问

02

认证

03

角色

04

数据访问控制

05

加密


一、访问


安全审查的第一部分是查看服务器是如何被连接和访问的。与任何安全配置一样,在考虑如何配置系统时,应遵循最小权限原则(https://en.wikipedia.org/wiki/Principle_of_least_privilege),即只允许工作系统所需的访问权限。


物理访问


防止拥有对服务器物理访问权限的人获取数据可能极为困难,但可以采取一些物理和技术措施。

首先,应尽可能限制物理访问,确保服务器位于安全的设施中。比如一个私有的服务器机房,在这种情况下,可以采取措施确保只有授权人员才能进入该房间,并且采用监控措施,如电视监控。如果使用共址设施,则应确保所选提供商有严格执行的安全策略,以防止未经授权的访问,并且在允许用户进入的设施中,提供锁定机架,以防止其他客户接触硬件。

对于主要的云服务提供商,除了信任他们声称实施的高级别物理安全措施外,几乎别无他法。然而,对于云服务提供商和共址设施,必须检查他们是否有适当的文件证明他们提供的安全级别,如SOC 2或3(https://www.datacenterknowledge.com/archives/2011/03/03/sas-70-ssae-16-soc-and-data-center-standards)。


连接


有两种方式可以连接到Postgres服务器:通过Unix域套接字或TCP/IP套接字。

  • Unix域套接字

Unix域套接字(UDS)是在类Unix平台上连接到Postgres数据库的默认方法。PostgreSQL 在 Windows 上从 15 版本开始正式支持 Unix Domain Sockets (UDS)。

UDS仅可从其所在的机器上访问(因此不会受到直接远程攻击),并且在文件系统中以特殊文件的形式出现。这意味着对它们的访问受到与其他文件相同的访问控制(尽管使用套接字仅需要写权限),并且可以通过unix_socket_permissions和unix_socket_group配置选项以及创建套接字的目录的权限来控制对它们的访问。套接字始终由运行Postgres服务器的用户拥有。

为了提供更大的灵活性,Postgres可以创建多个套接字(尽管默认情况下仅创建一个),使用unix_socket_directories配置选项,每个目录可以有不同的权限,以按需隔离不同的用户或应用程序,帮助应用最小权限原则。

如果应用程序与数据库服务器运行在同一主机上,请认真考虑仅通过一个或多个UDS允许对服务器的访问。

  • TCP/IP套接字

如果需要从远程系统访问Postgres服务器,这在实现具有多层或服务的应用程序时,或者仅用于使用pgAdmin等工具进行远程管理时,通常需要使用TCP/IP网络套接字。

通常情况下,就安全而言,希望最小化任何试图访问系统的人员的潜在攻击面。如何做到这一点取决于服务器在网络上的托管方式。如果它位于公司网络内部,它可能托管在多个VLAN或物理网络上,这些网络可用于不同目的,例如应用程序、管理和存储访问等。

系统应仅配置为在所需的网络上侦听并接受连接;默认情况下,Postgres的源代码构建将仅在localhost或回环地址上侦听,这防止了来自其他机器的连接。

使用postgresql.conf中的listen_addresses配置参数,确保Postgres仅在所需的网络地址上侦听并接受连接,从而防止来自其它网络的访问。


防火墙


防火墙是防止未经授权访问网络端口的重要工具。许多防火墙还提供日志记录功能,这些功能可以作为更广泛主动检测入侵尝试计划的一部分。

  • 本地机器

大多数操作系统都包含防火墙,包括Windows上的Windows Defender防火墙和Linux上的iptables;此外,还有许多第三方产品可供选择。

典型的防火墙将允许定义入站和出站规则,这些规则指定允许的流量。这些规则将由几个常见参数组成:

  • 协议,例如TCP或IPv6

  • 本地端口,例如5432(PostgreSQL的默认端口)

  • 源地址;即连接尝试来自何处。

一些防火墙提供额外的选项以提供更大的灵活性;例如,Windows Defender防火墙允许指定程序而不是端口号。

始终要最小化对Postgres的访问,因此通常会创建一条规则,允许来自应用程序服务器的地址TCP(和/或IPv6)流量在5432端口访问,而其它TCP(和/或IPv6)到达时被拒绝(或黑洞)。源地址通常可以是一组地址或子网。

如果服务器安装了任何外部数据包装器或类似的扩展,可能还需要创建出站规则,以防止它们连接到除预定义的一组服务器之外的任何内容。

虽然配置Windows Defender防火墙相当直接,但配置iptables要复杂得多。像Redhat和Ubuntu这样的Linux发行版提供了管理工具来简化这一过程,还有其他开源工具可用,如Ferm和Shorewall。

尽可能使用防火墙最小化对服务器的访问。

  • 云服务提供商

大多数云服务提供商建议不要在虚拟实例中使用防火墙,而是建议用户使用平台内置的防火墙。这通常使管理更加容易,允许创建可以重用并附加到多个服务器的规则集,并允许通过他们的Web界面、命令行界面和REST API进行管理。

云服务提供商的防火墙是作为其网络基础设施的一部分实现的,通常与上一节中描述的主机防火墙以相同的方式工作,即指定流量允许的源地址、协议和目标端口。

大多数云服务提供商还提供虚拟私有云(VPC),其中多个服务器可以在具有自己的私有网络或网络的单一虚拟环境中共同存在。这种配置已成为默认配置,使得在公共云上部署多层系统变得非常容易,同时将非公共层与互联网的其余部分隔离。

在VPC中使用多个子网可以轻松地进一步隔离服务器,将公共层放在“DMZ”子网中,而数据库服务器则放在没有直接互联网连接的私有子网中。


传输加密


如果数据库服务器的流量在网络中流动,那么对流量进行加密是一种很好的(可以说是必不可少的)做法。Postgres使用OpenSSL提供传输安全性。

要加密Postgres中的连接,至少需要一个服务器证书和密钥,理想情况下,该密钥通过密码保护,该密码可以在服务器启动时安全输入,无论是手动输入还是使用在服务器启动时代表服务器检索密码的脚本,如通过ssl_passphrase_command配置参数指定。服务器证书和密钥分别使用ssl_cert_file和ssl_key_file指定。

如果已经使用了现有的证书颁发机构(CA),则可以使用Postgres提供的证书。ssl_ca_file和ssl_crl_file配置参数允许将CA(和中间)证书和证书吊销列表提供给服务器。这能够灵活地在安全事件发生时撤销证书,并让服务器拒绝客户端证书或客户端拒绝服务器证书。它还允许配置客户端和服务器,如果无法通过信任链验证彼此的身份,则拒绝彼此,以防止尚未检测到的欺骗行为。在下面的客户端认证部分中会讨论使用证书进行客户端认证。

确保TLS使用也是安全的非常重要。有几个配置参数可以设置,以确保没有使用可能不再被认为是安全的密码或其他选项。建议检查并适当配置postgresql.conf配置文件中的以下配置参数:

  • ssl_ciphers

  • ssl_ecdh_curve

  • ssl_dh_params_file

  • ssl_min_protocol_version

本文未对这些参数应设置为何值提出建议,因为它们肯定会随时间而变化。我们应该定期检查,以确保使用的是仍然被认为是安全的选项,并在适当时进行更新。

如果服务器流量在网络中流动,请确保使用最强可能的密码和其他选项进行加密。


二、认证


在访问之后,下一个要考虑的安全组件是客户端认证,这是通过pg_hba.conf配置文件认证用户并控制他们是否可以成功连接到服务器的方式。


pg_hba.conf


pg_hba.conf文件定义了数据服务器的访问规则和认证方法。当建立连接时,文件中的行将按顺序处理,第一条匹配连接属性的行将用于确定认证方法。

该文件有七种可能的行格式(以及以#开头的注释),其中三种是主要变体,其余的结构与其中一种相同,但第一字段中的连接类型不同。以下是一些示例:

    local     my_db     my_user     scram-sha-256

    在这个示例中,my_user通过本地(UDS)连接使用scram-sha-256连接到my_db的尝试将被接受。

      host        my_db     my_user     172.16.253.47/32        md5

      在这个示例中,my_user从172.16.253.47使用md5作为认证方法连接到my_db的尝试将被接受。

      注意,示例中显示的地址以/32结尾,表示必须匹配所有32个高阶位。为了允许从该子网的任何地方匹配连接,也可以将其写为172.16.0.0/16或172.16.0.0 255.255.0.0(第三种格式变体)。

      每行中的字段始终是连接类型、数据库名称、用户名、客户端网络地址/子网(如需要)和认证方法,以及可能适用的任何选项。有关更多信息,请参阅文档。

      一般来说,任何网络连接都应使用hostssl或hostgssenc连接类型,以确保连接是加密的。


      信任(trust)


      信任trust认证方法只应在极特殊的情况下使用,如果有的话,因为它允许匹配的客户端无需进一步认证即可连接到服务器。trust适用于在本地机器上通过UDS进行连接时的测试和开发工作,以及当只有完全受信任的用户可以访问机器且数据安全不是问题时。

      它也是在没有其他登录方式时重置服务器密码的有用机制;暂时允许通过UDS的信任访问,连接到服务器并重置密码,然后再次禁用trust访问。

      使用trust时要极其小心。它可能非常危险!


      Peer 和 Ident


      Peer和ident都是允许用户通过底层操作系统进行认证的方法。许多Postgres软件包预先配置为使用对等认证。

      对等认证方法仅适用于本地连接。当使用peer时,服务器从操作系统获取客户端的用户名,并检查它是否与请求的数据库用户名匹配。

      ident认证方法仅适用于网络连接。它的工作方式与对等(peer)认证类似,只是它依赖于客户端上运行的ident服务器来确认用户名。

      Peer和ident都允许使用连接映射来处理客户端已知用户名与数据库服务器已知用户名之间的可接受不匹配。

      注意,不应依赖ident,因为运行ident服务器的客户端不太可能被保证是可信的。


      md5 与 SCRAM


      多年来,md5一直是与Postgres一起使用的首选哈希机制,尽管仍然广泛使用,但强烈建议用户在需要密码认证时转向scram-sha-256。

      md5和scram-sha-256都使用挑战响应机制来防止嗅探,并在服务器上存储哈希密码。然而,scram-sha-256以目前被认为是密码学安全的形式存储哈希,以避免如果攻击者获得哈希时出现问题。

      如果需要使用独立的Postgres服务器进行密码认证,应该使用scram-sha-256作为认证方法。不要在新部署中使用md5!


      LDAP 与 Kerberos


      在企业环境中,当与单点登录(SSO)系统集成时,通常会使用LDAP和Kerberos。在这些系统中,Postgres服务器被配置为通过LDAP目录或Kerberos基础设施认证用户。

      在LDAP系统中,有多种方式可以控制用户访问,例如,仅允许特定组织单位或组的成员访问。在设置pg_hba.conf文件时,可以在行的末尾指定额外选项,包括LDAP搜索过滤器,该过滤器将仅允许匹配过滤器的用户连接到数据库。

      Kerberos认证通过Postgres中的gssapi认证方法实现。设置它可能比LDAP和其他认证方法更具挑战性,但它被认为是安全的,并为支持它的客户端软件提供自动认证。还有一个相关的特定于Windows的sspi认证方法,可以在Windows域中使用。

      尽管LDAP认证非常受欢迎,但应始终优先选择Kerberos认证,因为与LDAP不同,用户的密码永远不会发送到Postgres服务器。


      TLS 证书


      TLS(有时称为SSL)证书可用于认证,并作为要求TLS加密的一部分。证书认证的工作原理是信任一个顶级证书(或其子证书或“中间”证书)仅向受信任的客户端颁发证书。拥有由更高权威机构颁发的证书和密钥的客户端,该权威机构也颁发了服务器证书和密钥,可以被视为受信任的。

      一个简单的例子,首先创建一个证书颁发机构(CA)证书和密钥。并且确保必须完全保密。然后,创建一个Postgres服务器证书和密钥,并使用CA证书和密钥对其进行签名。

      然后将服务器证书和密钥安装到Postgres服务器中,以及CA证书的副本(但不是CA的密钥)。

      然后也可以根据需要创建客户端证书和密钥,并由CA进行签名。

      当客户端使用cert认证方法连接到Postgres时,Postgres服务器将检查客户端呈现的证书是否受信任,以及证书中的通用名称(CN)字段是否与客户端的用户名匹配。也可以像peer和ident一样进行用户名映射。

      客户端在连接到服务器时也可以指定几个选项,包括是否(以及在何种程度上)验证服务器证书的可信度。这提供了防止欺骗的保护。

      客户端和服务器都可以使用证书吊销列表来跟踪任何不再受信任的证书。

      证书是认证需要通过网络连接到Postgres服务器的自动化系统的理想方式。


      额外配置


      还有两个额外的配置选项值得考虑:

      authentication_timeout是可以在postgresql.conf中设置的参数。其目的是设置认证必须在服务器关闭连接之前完成的最大时间。这是为了确保不完整的连接尝试不会无限期地占用连接槽。

      auth_delay是Postgres的一个模块,可以通过postgresql.conf中的shared_preload_libraries配置选项加载。其目的是在认证尝试失败后,在报告失败之前短暂暂停,使暴力攻击更加困难。


      其他认证方法


      上面介绍的是Postgres中一些更流行的认证方法。还有许多其他认证方法可用(https://www.postgresql.org/docs/17/client-authentication.html),但这些使用较少,且具有更特殊的应用。


      三、角色


      保障Postgres部署安全的下一个关键组件是角色的创建和设置,这些角色可以限制特定用户对数据库的访问。


      什么是角色?


      非常古老(几乎是史前的)版本的PostgreSQL提供了用户和用户组,作为将用户帐户分组的方式。在PostgreSQL 8.1中,这个系统被符合SQL标准的角色系统所取代。

      一个角色可以是其他角色的成员,也可以作为成员的角色。有时称这为“授予granting”一个角色给另一个角色。角色有几个可以设置的属性,包括一些实际上使它们成为可以用来登录数据库服务器的用户帐户的属性。以下是一个授予角色给另一个角色的示例:

        GRANT pg_monitor TO nagios;

        这使得nagios角色成为pg_monitor的成员,从而赋予nagios访问保留给超级用户和pg_monitor角色成员的扩展功能的权限。


        角色属性


        角色有几个固定的属性可以设置:

        • LOGIN: 这个角色是否可以用来登录数据库服务器?

        • SUPERUSER: 这个角色是否是超级用户?

        • CREATEDB: 这个角色是否可以创建数据库?

        • CREATEROLE: 这个角色是否可以创建新角色?

        • REPLICATION: 这个角色是否可以启动流复制?

        • PASSWORD: 角色的密码,如果设置了的话。

        • BYPASSRLS: 这个角色是否可以绕过行级安全检查?

        • VALID UNTIL: 一个可选的时间戳,之后密码将不再有效。

        设置了SUPERUSER标志的角色自动绕过所有权限检查,除了登录的权利。

        还有一些其他不太常用的角色属性可以设置。有关更多信息,请参阅文档(https://www.postgresql.org/docs/17/sql-createrole.html)。

        谨慎授予SUPERUSER(以及可能危险的属性,如CREATEDB和CREATEROLE)。不要使用具有SUPERUSER权限的角色进行日常工作。


        密码复杂度


        PostgreSQL(与EDB Postgres Advanced Server相反)默认不包含任何密码复杂度强制功能。它包含一个可以用来进行密码复杂度检查的钩子hook,但如果用户使用预哈希的字符串更改密码,这将没有任何效果。

        Postgres源树的contrib目录中可以找到一个密码检查模块,并且大多数软件包中都包含该模块。该模块可以用作开发更复杂的模块的示例,以满足组织的具体需求,尽管它确实需要C语言开发工作。

        在Postgres中强制密码复杂度的最有效方法是使用外部身份服务进行认证,如上文所述的LDAP或Kerberos。


        密码配置文件


        EDB Postgres Advanced Server提供了一个密码配置文件功能,该功能可以与pg_hba.conf中配置的密码(永远不要使用这个,因为密码将以明文形式传输!)、md5和scram-sha-256认证方法一起使用。密码配置文件可以由超级用户配置并应用于一个或多个角色。配置文件允许定义以下选项:

        • FAILED_LOGIN_ATTEMPTS: 在角色被锁定之前允许的失败登录尝试次数,锁定时间为PASSWORD_LOCK_TIME参数指定的时间。

        • PASSWORD_LIFE_TIME: 密码可以使用的天数,之后用户将被提示更改密码。

        • PASSWORD_GRACE_TIME: 密码过期后用户在被强制更改密码之前的宽限期长度。当宽限期到期后,用户将被允许连接,但在更新过期密码之前将不允许执行任何命令。

        • PASSWORD_REUSE_TIME: 用户在重新使用密码之前必须等待的天数。

        • PASSWORD_REUSE_MAX: 在密码可以重新使用之前必须发生的密码更改次数。

        • PASSWORD_VERIFY_FUNCTION: 可以检查密码复杂度的PL/SQL函数的名称。

        注意,如果用户通过提供预哈希形式的新密码来更改密码,那么就无法使用PASSWORD_VERIFY_FUNCTION选项验证复杂度或使用PASSWORD_REUSE_MAX选项进行重用。为了缓解这种情况,可以在密码配置文件中将PASSWORD_ALLOW_HASHED选项设置为false。

        如果正在运行EDB Postgres Advanced Server,并且不使用LDAP或Kerberos等外部认证提供程序,考虑使用密码配置文件以确保用户保持强密码,并定期更改密码。


        SET ROLE


        SET ROLE SQL命令可以由用户应用于将当前会话的用户标识符更改为他们所属的任何角色的名称。这可以用来增加或限制会话的权限,并且可以使用RESET ROLE重置(因此,SET ROLE不适合用作多租户解决方案)。

        SET ROLE类似于在类Unix系统上使用sudo su -。它本质上允许以另一个用户的身份运行SQL命令。

        当一个角色是另一个角色的成员时,它将自动继承该角色的权限。为了有效地使用SET ROLE,应在创建角色时使用NOINHERIT关键字,以防止它自动继承权限,需要使用SET ROLE明确获得权限。

        除了SET ROLE之外,还有一个SET SESSION AUTHORIZATION命令,该命令仅对超级用户可用。它们之间的主要区别是,SET ROLE将更改current_user值,但不会更改session_user,而SET SESSION AUTHORIZATION将同时更改这两个值。

        实际上,这意味着在运行SET SESSION AUTHORIZATION之后,任何后续的SET ROLE命令都将限制为session_user可以执行的操作,尽管原始session_user是超级用户。这允许超级用户更准确地模仿另一个用户。

        考虑使用SET ROLE允许用户仅在需要执行更具潜在危险性的任务时临时提升他们的权限。


        监控角色


        Postgres自带几个内置的监控角色,这些角色可以访问以前仅限超级用户使用的功能。这些角色允许将特定权限授予用于监控系统的角色,而无需赋予它们完整的超级用户访问权限:

        • pg_monitor: 一个结合了以下所有角色的角色:

        • pg_read_all_settings: 读取所有配置变量,包括通常只有超级用户可见的那些。

        • pg_read_all_stats: 读取所有pg_stat_*视图并使用各种与统计相关的扩展,包括通常只有超级用户可见的那些。

        • pg_stat_scan_tables: 执行可能对表持有ACCESS SHARE锁的监控函数,潜在地持续很长时间。

        使用监控角色为用于监控数据库服务器的角色提供提升的权限,以避免需要赋予它们超级用户访问权限。确保角色具有完成所需工作所需的最小权限。


        四、数据访问控制


        在检查Postgres部署的安全设置时,重要的是查看数据访问控制以及如何防止用户访问他们不应该访问的数据。


        ACLs


        访问控制列表(ACLs)是附加在Postgres中的对象(如表、函数、视图,甚至是列)上的有些晦涩的字符串。它们包含授予每个角色的权限列表,如select、insert、execute等,以及每个权限的额外可选标志(*),如果存在,表示该角色有权将此权限授予其他角色,以及授予权限的角色的名称。

        例如,由Joe创建的表的ACL可能如下所示:

          joe=arwdDxt/joe =r/joe sales_team=arw/joe

          第一部分表示Joe对该表拥有所有可用权限(INSERT、SELECT、UPDATE、DELETE、TRUNCATE、REFERENCES和TRIGGER),这些权限最初由Joe授予(当他创建表时)。

          第二部分表示,Joe授予了PUBLIC(一个特殊的伪角色,意味着每个人)对该表的读取权限,第三部分表示,销售团队被授予了INSERT、SELECT和UPDATE权限,同样由Joe授予。

          ACL中的权限标志会根据对象的类型而有很大差异;请参阅文档以获取更多详细信息(https://www.postgresql.org/docs/current/ddl-priv.html)。

          了解Postgres中ACL的写法很有用,特别是如果更喜欢使用命令行工具,这些工具通常会以内部格式显示它们。像pgAdmin这样的图形化工具会解析并以更易于阅读的视觉格式显示ACL。

          任何设计良好的系统都应该使用角色与ACL结合,以保护数据库中的架构和数据。一个好的做法是让schema(即表和其他对象)由一个非超级用户角色拥有,该角色不是应用程序用来连接数据库的角色,也不用来授予其他登录角色的权限。

          创建反映应用程序中权限或角色的组角色,并根据需要将这些角色授予登录角色。通常不建议直接向最终用户使用的登录角色授予权限,因为这很快就会变得难以管理。

          花时间充分了解系统中用户和应用程序完成工作所需的权限。最小化权限,仅提供所需的权限,将架构所有权与数据分离,并使用组角色简化单个登录角色的权限管理。


          GRANT和REVOKE


          在Postgres中,使用GRANT和REVOKE SQL命令管理对象上的ACL。在大多数情况下,创建对象时,只有所有者对该对象有任何权限,可以以任何方式使用或操作该对象,例外情况是PUBLIC被授予了函数和过程的EXECUTE权限、数据库的CONNECT和TEMPORARY权限以及语言、数据类型和域的USAGE权限。如果需要,可以撤销这些权限。

          修改或删除对象的权限始终保留给对象的所有者和超级用户。可以使用ALTER SQL命令重新分配对象所有权。

          可以使用ALTER DEFAULT PRIVILEGES命令为某些对象类型覆盖default privileges。这允许配置系统,以便在创建新对象时,某些权限会自动授予角色。例如,Joe在前面的例子中,可以发出以下命令,授予销售团队对任何新表(但不是现有表,这些表可能需要手动更新)的INSERT、SELECT和UPDATE权限:

            ALTER DEFAULT PRIVILEGES
            GRANT INSERTSELECTUPDATE
            ON TABLES
            TO sales_team;

            假设新创建的对象没有自动包括所需的权限在ACL中,可以使用GRANT和REVOKE按需设置ACL。继续前面的例子,Joe可能会使用以下SQL命令授予销售团队对orders表的权限:

              GRANT INSERTSELECTUPDATE ON orders TO sales_team;

              为了撤销任何自动授予的权限或根据变化的业务需求撤销以前授予的权限,可以使用REVOKE SQL命令:

                REVOKE UPDATE ON orders FROM sales_team;

                假设销售团队以前有INSERT、SELECT和UPDATE权限,如前面的例子所示,将撤销UPDATE权限,允许他们查看和添加订单,但不能修改它们。

                值得注意的是,列上的ACL有时会出现问题,因为SELECT * FROM查询中的通配符不会排除用户没有访问权限的列,而是会返回对该表的访问拒绝消息。在这种情况下,用户应该明确列出他们有权限SELECT的列。

                创建了组角色来组织登录用户后,使用GRANT和REVOKE SQL命令给予组角色完成工作所需的最小权限级别。在适当时使用默认权限作为节省时间的方法,但要小心,这样做不会给予比未来适当更多的权限。通过使登录角色成为组角色的成员,使用GRANT将权限给予所需的登录角色。


                RLS


                行级安全(RLS)是Postgres中的一种技术,允许定义限制表中行对某些角色可见性的策略。在深入探讨RLS策略的设置细节之前,需要注意两个重要的注意事项:

                1. 超级用户和具有BYPASSRLS属性的角色总是绕过行级安全策略,表的所有者也是如此,除非他们强迫策略对自己生效。

                2. 用户可能通过“隐蔽通道”推断出一行的存在。例如,字段上的唯一约束,如社会安全号码,可能阻止用户插入具有相同值的另一行。用户无法访问该行,但他们可以推断出具有该社会安全号码的记录已经存在。

                默认情况下,Postgres中的表行级安全是关闭的。可以使用ALTER TABLE…ENABLE ROW LEVEL SECURITY命令启用它,这将启用一个限制性策略,防止访问所有数据,除非或直到创建了其他策略。

                策略本身由一个名称、应用该策略的表、可选的应用该策略的角色以及USING子句组成,该子句定义如何识别匹配或允许的行。例如,限制对销售团队成员创建的订单的访问:

                  CREATE POLICY sales_team_policy ON orders TO sales_team USING(sales_person = current_user);

                  也可以指定策略适用的操作。以下示例将允许销售团队的所有成员选择任何订单,但只有原始销售人员才能更新或删除订单:

                    CREATE POLICY sales_team_select_policy ON users FOR SELECT USING(true);
                    CREATE POLICY sales_team_modify_policy ON users USING(sales_person = current_user);

                    默认情况下,使用允许性策略,这意味着当有多个策略适用时,它们将使用布尔OR组合。也可以使用限制性策略,在评估是否允许访问行时使用布尔AND。

                    行级安全策略可能需要一些努力来设置,索引设计也必须考虑它们,但在某些情况下,可能有必要这样做,例如在医疗记录系统中,可能有法律要求限制对患者记录的访问,仅限于直接负责患者护理的医疗人员。

                    考虑限制对每个表中特定行的访问的法律和道德要求,并在必要时设计和实施RLS策略以满足这些要求。注意通过避免在约束中使用敏感数据来最小化隐蔽通道。


                    视图


                    视图显然可以将常用的查询封装成一个可以像表一样查询的对象,但它们也可以用来防止未经授权的数据访问,通过确保角色没有从底层表中选择的能力,而是必须从视图中访问数据。一个经典的例子是Postgres中的pg_catalog.pg_authid表,该表为数据库中的每个角色包含一行,包括一个列,其中包含角色的密码的哈希值(如果已设置)。由于哈希被视为敏感信息,因此该表没有为除了初始化数据库的超级用户之外的任何角色提供SELECT权限。

                    相反,提供了一个视图(pg_catalog.pg_roles),任何用户都可以从中选择。当从视图中选择时,密码始终返回为********。这可能比简单地使用底层表上的密码列的ACL更方便,因为那样会在使用SELECT * FROM查询时导致权限错误。

                    当使用可更新视图时,在定义视图时可以使用CHECK OPTION。如果省略,视图将允许用户插入或更新记录,这些记录通过视图不可见。否则,只有当行对用户可见时,才允许插入或更新。如果指定了LOCAL CHECK OPTION,仅检查直接使用的视图上的条件,但当使用CASCADED CHECK OPTION时(如果指定了CHECK OPTION,则为默认值),将检查直接使用以及任何其他底层视图上的条件。

                    考虑使用安全表的视图作为允许适当角色访问基础表中的有限列子集的方法。


                    安全屏障


                    使用视图限制对列的访问是很常见的,但人们通常也使用它们来限制对某些行的访问。虽然这样做确实有价值,但必须注意一个副作用;可能会欺骗Postgres优化器泄露隐藏的数据!

                    这实际上并不是一个错误;这是系统设计的意图。本质上,可能发生的情况是,当用户执行对视图的查询,并且用户在该外部查询中调用了一个成本非常低的函数时,优化器可能会选择对视图底层的每一行数据运行查询,然后才应用视图中的选择性子句,从而允许函数访问受限数据。

                    使用安全屏障可以解决这个问题,这基本上是在创建视图时传递的一个选项。它告诉Postgres始终先执行视图上的限定条件,从而确保函数永远不会看到隐藏的行。

                    与安全屏障相关的是函数的LEAKPROOF参数。这只能由超级用户在创建函数时使用,用于证明该函数不会泄露任何信息,除了预期的返回值。允许Postgres更好地优化使用安全屏障视图的查询中的函数,确信该函数不会泄露任何信息。

                    在使用视图隐藏行时要小心,确保它们被标记为安全屏障,以避免泄露数据。考虑RLS是否可能是限制对特定行访问的更好解决方案。


                    安全定义函数


                    默认情况下,Postgres中的函数和过程被称为SECURITY INVOKER函数。这意味着当它们被调用时,它们以调用角色的权限执行。

                    在创建函数时传递SECURITY DEFINER选项意味着每当函数被调用时,它将始终以所有者的权限而不是调用角色的权限执行。这类似于Unix文件ACL中的setuid位,当设置时,它允许可执行文件以其所有者的权限而不是执行它的用户的权限运行。

                    这种能力非常有用。一个例子可能是一个函数,它被表上的触发器调用来向审计日志写入一条记录,该日志阻止所有登录和组角色以任何方式访问。在使用SECURITY DEFINER函数时,重要的是要仔细考虑其后果——特别是,确保它们尽可能简单,并且只执行一个任务,而不接受任何可能使它们被用于其他目的的参数。

                    考虑使用SECURITY DEFINER函数为无法直接执行该任务的角色提供特定功能。小心考虑可能的后果以及这些函数可能被滥用的方式,并确保它们仅限于执行预期任务。


                    数据脱敏


                    数据脱敏是通过动态更改显示的值来隐藏特定的敏感信息片段。虽然如上所述,可以在Postgres中使用视图在一定程度上实现这一点,但EDB Postgres Advanced Server提供了原生的数据脱敏功能。

                    在EPAS中,通过表上的数据脱敏策略实现脱敏。简而言之,这些策略指定一个或多个表上的列,一个表达式来确定是否应用策略,一个执行脱敏的函数,一个范围,以及对策略的任何例外。请参阅上面的文档链接,了解如何创建策略的示例。

                    当使用EDB Postgres Advanced Server并且处理敏感数据(如信用卡号)时,考虑使用数据脱敏策略,将显示的数据动态更改为脱敏形式,例如“XXXX XXXX XXXX XXXX 8397”,以防止用户不必要地访问敏感数据。


                    五、加密


                    在为Postgres实现端到端安全时要考虑的最后一个组成部分是加密敏感数据。有几种方法和扩展可用,可以配置加密提供额外的安全性。


                    pgcrypto


                    pgcrypto是Postgres和EPAS的标准扩展,作为source树和大多数二进制发行版中的contrib模块包含。其目的是提供SQL函数用于加密和哈希,这些函数可以作为数据库设计逻辑的一部分使用。


                    1. 安装

                    在大多数Postgres的二进制发行版中,可以通过首先确保服务器上安装了contrib模块来安装pgcrypto。基于安装程序的发行版,如Windows和macOS的发行版,通常作为数据库服务器本身的一部分安装。像Debian/Ubuntu的DEBs和Redhat/SUSE的RPMs这样的Linux软件包可能将它们包含在一个子软件包中。例如,来自yum.postgresql.org的PostgreSQL社区软件包有一个名为postgresql17-contrib的软件包,用于PostgreSQL 17。

                    一旦在服务器上安装了软件包,只需在所需的数据库中作为超级用户运行CREATE EXTENSION命令:

                      CREATE EXTENSION pgcrypto;

                      在需要哈希或加密单个数据片段以满足法规等要求时,考虑在数据库中使用pgcrypto扩展。


                      2. 哈希

                      哈希是一种生成数据片段的密码学安全表示的方法,通常具有固定长度(长度取决于所使用的算法)。重要的是,它是不可逆的;即,无法从哈希值中提取原始数据——然而,由于哈希值对原始数据是唯一的,因此它可以作为校验和,用于查看数据是否被更改,或者查看用户提供的值是否与原始值匹配。

                      哈希主要用于存储密码和其他可能需要验证但不需要返回的敏感信息。

                      例如,可以使用pgcrypto对用户将来将使用的密码进行哈希:

                        INSERT INTO users
                            (username, email, password)
                        VALUES
                            ('pgsnake''dave.page@enterprisedb.com', crypt('new password', gen_salt('md5')));

                        要稍后验证此密码,可以从表中选择用户记录:

                          SELECT
                              *
                          FROM
                              users
                          WHERE
                             username = 'pgsnake' AND
                             password = crypt('entered password', password)

                          如果返回了记录,则密码输入正确——否则,密码输入错误。

                          需要注意的是,当密码包含在SQL命令中时,如上所示,它们可能会被写入数据库服务器上的日志文件中。如果网络通信未受到加密保护,这些命令也可能会泄露。

                          永远不要以明文或混淆形式在数据库中存储用户密码,也永远不要使用可逆的加密形式,除非应用程序的功能绝对需要(例如,编写密码管理器应用程序)。尽可能使用不可逆的哈希来存储应用程序密码和其他需要验证但不需要返回的信息。


                          3. 加密

                          pgcrypto还提供了加密数据的功能,这在存储将来需要检索但应以安全形式存储的信息时非常有用。pgcrypto提供了“原始”加密/解密函数以及PGP函数。

                          强烈推荐使用PGP函数,而不是使用原始函数,后者直接使用用户提供的密钥作为密码,不提供完整性检查,期望用户管理所有加密参数,并且处理的是字节数据,而不是文本。

                          对称密钥加密最容易使用,因为它不需要PGP密钥。例如,可以使用以下简单的SQL命令演示数据的加密和解密,其中内部函数调用加密数据,外部函数解密:

                            SELECT pgp_sym_decrypt(
                                pgp_sym_encrypt('Hi There''password'),
                                'password');

                            注意,加密函数返回的密文并传递给解密函数的是字节数据格式。

                            要使用公钥功能,首先需要一个密钥。可以使用GnuPG生成,例如使用以下命令:

                              gpg --gen-key

                              PostgreSQL文档建议首选的密钥类型是“DSA和Elgamal”。生成密钥后,需要将其导出:

                                # 列出密钥环中的密钥:
                                gpg --list-secret-keys
                                # 以ASCII护甲格式导出公钥:
                                gpg -a --export KEYID > public.key
                                # 以ASCII护甲格式导出私钥:
                                gpg -a --export-secret-keys KEYID > secret.key

                                现在可以使用公钥通过SQL加密函数加密数据:

                                  pgp_pub_encrypt('<data>''<public key>')

                                  同样,可以稍后使用以下命令解密数据:

                                    pgp_pub_decrypt(<cipher text>, '<private key>')

                                    同样,注意密文是以字节数据格式的。

                                    在存储将来需要检索的敏感数据时使用加密。仔细考虑对称加密或公钥加密哪种最适合。一般来说,当与他人交换数据时(因为没有共享密钥),公钥更有意义,而对于自包含的应用程序,对称加密可能更有意义。


                                    4. 密钥KEY管理

                                    在数据库中使用加密的一个主要问题是密钥key管理。在其最基本的形式中,应用程序可能有一个硬编码或集中配置的密钥,它在加密和解密数据时使用。除非应用程序可以更改密钥,否则该密钥将一直有效,这也意味着所有用户共享一个单一密钥。这些因素大大增加了该密钥被多人知晓的可能性(例如,应用程序的管理员),其中一些人可能离开组织,带来风险。

                                    密钥管理系统通过提供将密钥存储在安全服务中,与数据库和应用程序分开,并可能为不同用户或目的使用不同密钥的方法,缓解了其中一些问题。一些,如Bruce Momjian的pgcryptokey扩展,还提供了通过SQL命令重新加密的功能。

                                    密钥管理系统还可以避免用户看到实际密钥的需要;他们对密钥的访问可以通过密码或密码短语(这可以通过Kerberos或类似的基于企业身份管理系统进行身份验证)进行控制,密钥本身根据需要直接传递给数据库服务器或应用程序。


                                    5. 文件系统和全盘加密

                                    当使用文件系统加密或全盘加密时,通常加密用于存储数据库和预写日志的卷,或者通常是整个系统。这些类型的加密对数据库服务器是透明的,并且在Postgres中不需要配置。

                                    需要注意的是,Postgres中的文件系统和数据加密保护的是不同的攻击向量。操作系统可能会在启动阶段非常早地使用密码或密钥管理系统来确保密钥保持在外部,但一旦带有文件系统加密的服务器启动并运行,挂载了文件系统,所有数据都可以像在没有加密的机器上一样访问。

                                    这种方式提供了对非运行硬件的物理攻击的保护,例如被盗硬盘。文件系统或全盘加密不保护对正在运行的系统的攻击,它们也不能够控制数据库中不同用户的数据可见性。

                                    大多数操作系统都提供了不同的文件系统或全盘加密选项,无论是商业产品还是开源产品。其中一些最常见的选项包括Apple macOS中包含的FileVault、用于Microsoft Windows的BitLocker以及Linux系统上的LUKS。

                                    所有主要的云服务提供商都提供了加密卷,以保护数据。例如,亚马逊的EBS提供了一个选项,用于创建加密卷,可以使用默认密钥或通过他们的密钥管理系统提供的密钥。

                                    值得注意的是,亚马逊当然可以访问您的密钥以及这些卷被配置的物理设备,但他们采取措施确保有权限访问密钥的员工和可能访问硬件的员工之间有职责分离。


                                    六、结论


                                    在本文中,从客户端角度审视了Postgres实现的安全性,一直到磁盘上的存储。同时描述了与服务器访问相关的几个因素如何影响Postgres服务器的安全性,并且在任何部署或审查中都应考虑以下内容:

                                    • 物理访问

                                    • 通过Unix域套接字和网络对服务器的访问

                                    • 防火墙

                                    • 传输加密

                                    同时还看到了确定用户认证机制以认证不同的连接尝试对于保障Postgres部署的安全重要性。角色也是Postgres中安全性的一个重要部分,通过正确配置和保障,就可以使用最小权限原则来最小化数据库服务器的风险。

                                    保障和最小化对Postgres中敏感数据的访问的技术需要规划和精心设计,但可以显著提高数据的安全性。最后,介绍了Postgres部署的加密安全性涵盖了数据和文件系统/全盘加密;可能还需要与密钥管理系统集成。

                                    在这一过程的所有方面中,有些选项适用于一种部署,而其他选项适用于其他部署。还应考虑其他功能,例如sepgsql——它可以与SELinux一起工作。

                                    最重要的,每个部署场景都是独一无二的。



                                    关于公司

                                    感谢您关注新智锦绣科技(北京)有限公司!作为 Elastic 的 Elite 合作伙伴及 EnterpriseDB 在国内的唯一代理和服务合作伙伴,我们始终致力于技术创新和优质服务,帮助企业客户实现数据平台的高效构建与智能化管理。无论您是关注 Elastic 生态系统,还是需要 EnterpriseDB 的支持,我们都将为您提供专业的技术支持和量身定制的解决方案。


                                    欢迎关注我们,获取更多技术资讯和数字化转型方案,共创美好未来!

                                    Elastic 微信群

                                    EDB 微信群


                                    发现“分享”“赞”了吗,戳我看看吧


                                    文章转载自新智锦绣,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                    评论