当前位置: 首页 > news >正文

PostgreSQL 16 Administration Cookbook 读书笔记:第6章 Security

PostgreSQL 安全性概述

PG 文档中没有专门的一章讲安全,散落在各处。可以参考老版本的文档或Security Information。

PostgreSQL 超级用户

postgres肯定是SUPERUSER:

postgres=# \du postgresList of rolesRole name |                         Attributes
-----------+------------------------------------------------------------postgres  | Superuser, Create role, Create DB, Replication, Bypass RLS

SUPERUSER会绕过所有权限检查,除了登录权限之外。这是一种危险的权限,不应随意使用;最好以非超级用户的角色完成大部分工作。

SUPERUSER是role的属性之一,其他属性包括Create DB, Create role。

postgres=# \h create role
Command:     CREATE ROLE
Description: define a new database role
Syntax:
CREATE ROLE 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' | PASSWORD NULL| VALID UNTIL 'timestamp'| IN ROLE role_name [, ...]| IN GROUP role_name [, ...]| ROLE role_name [, ...]| ADMIN role_name [, ...]| USER role_name [, ...]| SYSID uidURL: https://www.postgresql.org/docs/16/sql-createrole.html

撤销用户对表的访问权限

对表有访问权限,可能是以下三种情况:

  1. SUPERUSER
  2. 表的owner
  3. 被赋予了表的权限

如果不是SUPERUSER,也不是表的owner,要剥夺对表的访问权限,首先可以:

REVOKE ALL ON table1 FROM username;

但这样可能还不够,如果username被赋予了其他的role,而这个role对表有访问权限,那么就必须进行一下的操作之一:

  • 剥夺username的role
  • 剥夺role对表的访问权限

可能用到的psql元命令:

  • \z\dp,现实访问权限
  • \u,显示role

例如:

sampledb=> \conninfo
You are connected to database "sampledb" as user "sh" via socket in "/run/postgresql" at port "5432".
sampledb=> \z hr.employeesAccess privilegesSchema |   Name    | Type  | Access privileges | Column privileges | Policies
--------+-----------+-------+-------------------+-------------------+----------hr     | employees | table | hr=arwdDxt/hr    +|                   ||           |       | sh=r/hr           |                   |
(1 row)sampledb=> \du shList of rolesRole name | Attributes
-----------+------------sh        |

那么,sh没有赋予其他的role,直接收回权限就好了。

sampledb=> \c sampledb postgres
You are now connected to database "sampledb" as user "postgres".sampledb=# revoke all on hr.employees from sh;
REVOKE
sampledb=# \z hr.employeesAccess privilegesSchema |   Name    | Type  | Access privileges | Column privileges | Policies
--------+-----------+-------+-------------------+-------------------+----------hr     | employees | table | hr=arwdDxt/hr     |                   |
(1 row)

几个最佳实践:

  • 对于生产系统,通常最好在数据库创建脚本中始终包含 GRANT 和 REVOKE 语句,以便确保只有正确的用户才能访问该表。
  • 撤销或授予权限时,应使用完全限定名称;否则,您可能会无意中使用错误的表。

构建安全的视图

视图可以实现对表权限的屏蔽,例如hr schema中有一个表employees和一个视图low_salary_emps,赋予用户sh查看视图的权限:

sampledb=> create view low_salary_emps as select * from employees where salary <= 2200;
CREATE VIEWsampledb=> grant usage on schema hr to sh;
GRANTsampledb=> grant select on low_salary_emps hr to sh;
GRANT

则用户hr可以看到视图的内容,但不能直接访问表:

sampledb=> \z hr.employees;Access privilegesSchema |   Name    | Type  | Access privileges | Column privileges | Policies
--------+-----------+-------+-------------------+-------------------+----------hr     | employees | table | hr=arwdDxt/hr     |                   |
(1 row)sampledb=> \z hr.low_salary_emps;Access privilegesSchema |      Name       | Type | Access privileges | Column privileges | Policies
--------+-----------------+------+-------------------+-------------------+----------hr     | low_salary_emps | view | hr=arwdDxt/hr    +|                   ||                 |      | sh=r/hr           |                   |
(1 row)sampledb=> select * from hr.low_salary_emps;employee_id | first_name | last_name  |  email   | phone_number | hire_date  |  job_id  | salary  | commission_pct | manager_id | department_id
-------------+------------+------------+----------+--------------+------------+----------+---------+----------------+------------+---------------128 | Steven     | Markle     | SMARKLE  | 650.124.1434 | 2008-03-08 | ST_CLERK | 2200.00 |                |        120 |            50132 | TJ         | Olson      | TJOLSON  | 650.124.8234 | 2007-04-10 | ST_CLERK | 2100.00 |                |        121 |            50136 | Hazel      | Philtanker | HPHILTAN | 650.127.1634 | 2008-02-06 | ST_CLERK | 2200.00 |                |        122 |            50
(3 rows)sampledb=> select * from hr.employees;
ERROR:  permission denied for table employees

但是,视图对底层表的保护不是完全的。假设用户sh创建了以下的函数(此例子来自官网):

CREATE FUNCTION tricky(text, text) RETURNS bool AS $$
BEGINRAISE NOTICE '% => %', $1, $2;RETURN true;
END;
$$ LANGUAGE plpgsql COST 0.0000000000000000000001;

执行以下的语句,即可获取底层表的信息:

sampledb=> select * from hr.low_salary_emps where tricky(first_name, last_name);
NOTICE:  Steven => King
NOTICE:  Neena => Kochhar
NOTICE:  Lex => De Haan
NOTICE:  Alexander => Hunold
NOTICE:  Bruce => Ernst
NOTICE:  David => Austin
NOTICE:  Valli => Pataballa
NOTICE:  Diana => Lorentz
NOTICE:  Nancy => Greenberg
NOTICE:  Daniel => Faviet
NOTICE:  John => Chen
NOTICE:  Ismael => Sciarra
NOTICE:  Jose Manuel => Urman
NOTICE:  Luis => Popp
NOTICE:  Den => Raphaely
NOTICE:  Alexander => Khoo
NOTICE:  Shelli => Baida
NOTICE:  Sigal => Tobias
NOTICE:  Guy => Himuro
NOTICE:  Karen => Colmenares
NOTICE:  Matthew => Weiss
NOTICE:  Adam => Fripp
NOTICE:  Payam => Kaufling
NOTICE:  Shanta => Vollman
NOTICE:  Kevin => Mourgos
NOTICE:  Julia => Nayer
NOTICE:  Irene => Mikkilineni
NOTICE:  James => Landry
NOTICE:  Steven => Markle
NOTICE:  Laura => Bissot
NOTICE:  Mozhe => Atkinson
NOTICE:  James => Marlow
NOTICE:  TJ => Olson
NOTICE:  Jason => Mallin
NOTICE:  Michael => Rogers
NOTICE:  Ki => Gee
NOTICE:  Hazel => Philtanker
NOTICE:  Renske => Ladwig
NOTICE:  Stephen => Stiles
NOTICE:  John => Seo
NOTICE:  Joshua => Patel
NOTICE:  Trenna => Rajs
NOTICE:  Curtis => Davies
NOTICE:  Randall => Matos
NOTICE:  Peter => Vargas
NOTICE:  John => Russell
NOTICE:  Karen => Partners
NOTICE:  Alberto => Errazuriz
NOTICE:  Gerald => Cambrault
NOTICE:  Eleni => Zlotkey
NOTICE:  Peter => Tucker
NOTICE:  David => Bernstein
NOTICE:  Peter => Hall
NOTICE:  Christopher => Olsen
NOTICE:  Nanette => Cambrault
NOTICE:  Oliver => Tuvault
NOTICE:  Janette => King
NOTICE:  Patrick => Sully
NOTICE:  Allan => McEwen
NOTICE:  Lindsey => Smith
NOTICE:  Louise => Doran
NOTICE:  Sarath => Sewall
NOTICE:  Clara => Vishney
NOTICE:  Danielle => Greene
NOTICE:  Mattea => Marvins
NOTICE:  David => Lee
NOTICE:  Sundar => Ande
NOTICE:  Amit => Banda
NOTICE:  Lisa => Ozer
NOTICE:  Harrison => Bloom
NOTICE:  Tayler => Fox
NOTICE:  William => Smith
NOTICE:  Elizabeth => Bates
NOTICE:  Sundita => Kumar
NOTICE:  Ellen => Abel
NOTICE:  Alyssa => Hutton
NOTICE:  Jonathon => Taylor
NOTICE:  Jack => Livingston
NOTICE:  Kimberely => Grant
NOTICE:  Charles => Johnson
NOTICE:  Winston => Taylor
NOTICE:  Jean => Fleaur
NOTICE:  Martha => Sullivan
NOTICE:  Girard => Geoni
NOTICE:  Nandita => Sarchand
NOTICE:  Alexis => Bull
NOTICE:  Julia => Dellinger
NOTICE:  Anthony => Cabrio
NOTICE:  Kelly => Chung
NOTICE:  Jennifer => Dilly
NOTICE:  Timothy => Gates
NOTICE:  Randall => Perkins
NOTICE:  Sarah => Bell
NOTICE:  Britney => Everett
NOTICE:  Samuel => McCain
NOTICE:  Vance => Jones
NOTICE:  Alana => Walsh
NOTICE:  Kevin => Feeney
NOTICE:  Donald => OConnell
NOTICE:  Douglas => Grant
NOTICE:  Jennifer => Whalen
NOTICE:  Michael => Hartstein
NOTICE:  Pat => Fay
NOTICE:  Susan => Mavris
NOTICE:  Hermann => Baer
NOTICE:  Shelley => Higgins
NOTICE:  William => Gietzemployee_id | first_name | last_name  |  email   | phone_number | hire_date  |  job_id  | salary  | commission_pct | manager_id | department_id
-------------+------------+------------+----------+--------------+------------+----------+---------+----------------+------------+---------------128 | Steven     | Markle     | SMARKLE  | 650.124.1434 | 2008-03-08 | ST_CLERK | 2200.00 |                |        120 |            50132 | TJ         | Olson      | TJOLSON  | 650.124.8234 | 2007-04-10 | ST_CLERK | 2100.00 |                |        121 |            50136 | Hazel      | Philtanker | HPHILTAN | 650.127.1634 | 2008-02-06 | ST_CLERK | 2200.00 |                |        122 |            50
(3 rows)

这是由于tricky函数的成本非常低,因此planner选择优先执行他。即使用户被禁止定义新函数,内置函数也可用于类似的攻击。

要避免此问题,需要重新建立视图:

create or replace view low_salary_emps WITH (security_barrier) as select * from employees where salary <= 2200;

这回没有问题了:

sampledb=> select * from hr.low_salary_emps where tricky(first_name, last_name);
NOTICE:  Steven => Markle
NOTICE:  TJ => Olson
NOTICE:  Hazel => Philtankeremployee_id | first_name | last_name  |  email   | phone_number | hire_date  |  job_id  | salary  | commission_pct | manager_id | department_id
-------------+------------+------------+----------+--------------+------------+----------+---------+----------------+------------+---------------128 | Steven     | Markle     | SMARKLE  | 650.124.1434 | 2008-03-08 | ST_CLERK | 2200.00 |                |        120 |            50132 | TJ         | Olson      | TJOLSON  | 650.124.8234 | 2007-04-10 | ST_CLERK | 2100.00 |                |        121 |            50136 | Hazel      | Philtanker | HPHILTAN | 650.127.1634 | 2008-02-06 | ST_CLERK | 2200.00 |                |        122 |            50
(3 rows)

当视图需要提供行级安全性时,应为视图应用 security_barrier 属性。这可以防止恶意选择的函数和操作符在视图完成其工作之前从行传递值。

授予用户对表的访问权限

需要两个权限:

  • 对表所在的schema的USAGE权限
  • 对表的操作权限

也可以间接赋权,即先赋予权限给GROUP role,然后将GROUP role的权限赋予USER role。

授予用户对特定列的访问权限

语法为:

GRANT privileges (col1, col2, ... colN) ON tablename TO role; 

注意,如果赋予了表的某权限,则拥有表的当前和未来所有列的某权限。

授予用户对特定行的访问权限

需要启用RLS(Row Level Security)并创建RLS policy,详见这里。

创建新用户

使用createuser命令行或CREATE USER SQL命令。

$ createuser --interactive
Enter name of role to add: amy
Shall the new role be a superuser? (y/n) n
Shall the new role be allowed to create databases? (y/n) n
Shall the new role be allowed to create more new roles? (y/n) n
[postgres@ol9-vagrant test]$ psql
psql (16.9)
Type "help" for help.Try \? for help.
postgres=# \du amyList of rolesRole name | Attributes
-----------+------------amy       |

暂时阻止用户连接

ALTER USER role_specification LOGIN | NOLOGIN;

也可以现在用户的并发连接数,默认是-1,即无限制:

ALTER USER role_specification CONNECTION LIMIT connlimit;

默认所有用户对数据库有 CONNECT 权限,除非被显式收回。

REVOKE CONNECT ON DATABASE dbname FROM username;

检查权限,详见这里:

sampledb=# SELECT has_database_privilege('hr', 'sampledb', 'CONNECT');has_database_privilege
------------------------t
(1 row)

用户改为NOLOGIN后,强制终止现有连接。其实也可用于可以登录的用户:

SELECTpg_terminate_backend(pid)
FROMpg_stat_activity aJOIN pg_roles r ON a.usename = r.rolnameAND NOT rolcanlogin;

删除用户但不删除其数据

用户如果其下有对象,是不能直接删除的。

sampledb=# drop user hr;
ERROR:  role "hr" cannot be dropped because some objects depend on it
DETAIL:  owner of schema hr
owner of function hr.add_job_history(numeric,date,date,character varying,numeric)
owner of function hr.secure_dml()
...

可以将其下的对象转到其他用户:

REASSIGN OWNED BY hr TO new_owner;

也可以先删除其下所有对象,再删用户(不建议):

DROP OWNED BY hr;
DROP USER hr;

检查所有用户是否拥有安全密码

口令加密应为SCRAM-SHA-256,而非MD5:

SELECTusename,passwd
FROMpg_shadow
WHEREpasswd NOT LIKE 'SCRAM%'OR passwd IS NULL;

也可参考这里,这是一个口令复杂度验证函数,不能单独调用。

授予特定用户的有限超级用户权限

如果赋予了CREATE ROLE权限,那么即使没有CREATE DATABASE 权限,他也可以创建一个具有CREATE DATABASE权限的role。

还有一个函数的权限,详见这里:

SECURITY INVOKER indicates that the function is to be executed with the privileges of the user that calls it. That is the default. SECURITY DEFINER specifies that the function is to be executed with the privileges of the user that owns it.

PG还有很多预先定义好的role,详见Predefined Roles。

审计数据库访问

对表和列的权限详见Access Privilege Inquiry Functions中的has_table_privilege和has_column_privilege 。

审计可以用log_statement配置参数和pgaudit扩展。

也可参考博客Learning PostgresSQL读书笔记: 第14章 Logging and Auditing。

还有一个audit-trigger,利用触发器实现的,可参考Audit_trigger_91plus,91plus表示9.1版本以上支持。

了解当前登录用户

postgres=# select current_user, session_user;current_user | session_user
--------------+--------------postgres     | postgres
(1 row)postgres=# set role hr;
SET
postgres=> select current_user, session_user;current_user | session_user
--------------+--------------hr           | postgres
(1 row)postgres=> reset role;
RESET
postgres=# select current_user, session_user;current_user | session_user
--------------+--------------postgres     | postgres
(1 row)

与 LDAP 集成

这个话题较大,参考这里和这里。

使用加密连接(SSL/GSSAPI)

略,过程较复杂,涉及配置参数和pg_hba.conf,还有证书和SSL key。

postgres=# show ssl
ssl                                     ssl_crl_dir                             ssl_key_file                            ssl_passphrase_command
ssl_ca_file                             ssl_crl_file                            ssl_library                             ssl_passphrase_command_supports_reload
ssl_cert_file                           ssl_dh_params_file                      ssl_max_protocol_version                ssl_prefer_server_ciphers
ssl_ciphers                             ssl_ecdh_curve                          ssl_min_protocol_version

使用 SSL 证书进行身份验证

略。

将外部用户名映射到数据库角色

略,详见User Name Maps。

使用列级加密

即pgcrypto,这是个函数,和Oracle原生的TDE不同,意味着还是要改应用。

使用预定义角色设置云安全

略。

http://www.dtcms.com/a/279474.html

相关文章:

  • DLL 文件 OSError: [WinError 1401] 应用程序无法启动问题解决
  • 七、深度学习——RNN
  • HTTPS 协议原理
  • ZYNQ双核通信终极指南:FreeRTOS移植+OpenAMP双核通信+固化实战
  • 一文明白AI、AIGC、LLM、GPT、Agent、workFlow、MCP、RAG概念与关系
  • 浏览器防录屏是怎样提高视频安全性?
  • 现有医疗AI记忆、规划与工具使用的创新路径分析
  • 【Linux网络】多路转接poll、epoll
  • vue3 JavaScript 获取 el-table 单元格 赋红色外框
  • mac上用datagrip连接es
  • MFC/C++语言怎么比较CString类型最后一个字符
  • K8S的平台核心架构思想[面向抽象编程]
  • LVS(Linux Virtual Server)集群技术详解
  • linux 内核: 访问当前进程的 task_struct
  • 【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 架构搭建
  • C++-linux 6.makefile和cmake
  • 深入掌握Performance面板与LCP/FCP指标优化指南
  • 学习笔记——农作物遥感识别与大范围农作物类别制图的若干关键问题
  • 计算两个经纬度之间的距离(JavaScript 实现)
  • HashMap的长度为什么要是2的n次幂以及HashMap的继承关系(元码解析)
  • 前缀和题目:使数组互补的最少操作次数
  • 闲庭信步使用图像验证平台加速FPGA的开发:第十四课——图像二值化的FPGA实现
  • 如何集成光栅传感器到FPGA+ARM系统中?
  • JVM 内存模型详解:GC 是如何拯救内存世界的?
  • Oracle Virtualbox 虚拟机配置静态IP
  • 《亿级流量系统架构设计与实战》通用高并发架构设计 读场景
  • 1. 深入理解ArrayList源码
  • ae如何安装在非C盘
  • 7.15 窗口函数 | 二分 | 位运算
  • 逻辑代数中的基本规则,代入规则和反演规则,对偶规则