PostgreSQL Bài 5: Phân quyền và Bảo mật: Role, User, Grant, Revoke và xác thực qua file pg_hba.conf
Trong quản trị cơ sở dữ liệu thực tế, việc sử dụng tài khoản postgres (hoặc siêu người dùng SUPERUSER) cho ứng dụng backend kết nối trực tiếp là một lỗ hổng bảo mật nghiêm trọng. Nếu ứng dụng bị dính lỗi SQL Injection, kẻ tấn công có thể xóa sạch database hoặc thậm chí thực thi lệnh hệ điều hành thông qua extension copy program.
Bài học này sẽ đi sâu vào mô hình bảo mật 2 lớp của PostgreSQL:
-
Lớp ngoài (Network & Authentication): Kiểm soát ai được phép kết nối từ đâu qua
pg_hba.conf. -
Lớp trong (Authorization & Permissions): Kiểm soát người dùng đó được xem, sửa những đối tượng nào thông qua cơ chế
ROLE,GRANT,REVOKEvà kế thừa quyền.
1. Bản chất của "Role" và "User" trong PostgreSQL
Nếu từng làm việc với MySQL hay Oracle, bạn sẽ quen với việc phân biệt rõ: User (người dùng để đăng nhập) và Role (tập hợp quyền hạn gán cho user).
Quy tắc cốt lõi: Trong PostgreSQL, User và Role thực chất là một. Khái niệm
USERchỉ là một bí danh (alias) cú pháp cho mộtROLEcó quyền đăng nhập (LOGIN).
SQL
-- Hai câu lệnh này hoàn toàn tương đương nhau về mặt bản chất:
CREATE ROLE app_user WITH LOGIN PASSWORD 'strong_password';
CREATE USER app_user WITH PASSWORD 'strong_password';
Các thuộc tính quan trọng khi tạo Role
-
LOGIN/NOLOGIN: Xác định role này có thể dùng để xác thực kết nối hay không. Role dùng làm nhóm quyền (Group Role) thường mang cờNOLOGIN. -
SUPERUSER/NOSUPERUSER: Bỏ qua mọi cơ chế kiểm tra quyền. Cần tránh gán thuộc tính này cho các service backend thông thường. -
CREATEDB/NOCREATEDB: Quyền được tạo cơ sở dữ liệu mới. -
INHERIT/NOINHERIT: Quyền tự động thừa hưởng toàn bộ đặc quyền từ các group role mà nó tham gia (mặc định làINHERIT).
2. Mô hình phân quyền chuẩn trong Production: RBAC (Role-Based Access Control)
Không bao giờ cấp quyền trực tiếp cho từng user cá nhân. Thay vào đó, hãy thiết kế các Group Roles đại diện cho vai trò (ví dụ: chỉ đọc read_only, đọc-ghi read_write) rồi gán các user vào role đó.
[ Group: role_read_write ] (NOLOGIN)
│ (Thừa kế quyền)
▼
[ User: app_backend ] (LOGIN)
Kịch bản thực hành: Thiết lập phân quyền từ đầu
Giả sử chúng ta có database ecommerce_db và schema orders.
Bước 1: Tạo Group Roles và User
SQL
-- Tạo các Role đại diện nhóm quyền (không cho phép login trực tiếp)
CREATE ROLE read_only_group WITH NOLOGIN;
CREATE ROLE read_write_group WITH NOLOGIN;
-- Tạo User cụ thể cho ứng dụng backend kết nối
CREATE ROLE order_service_user WITH LOGIN PASSWORD 'Backend_Secure_Pass_2026' INHERIT;
-- Gán user vào nhóm read_write_group
GRANT read_write_group TO order_service_user;
Bước 2: Cấp quyền phân tầng (Database -> Schema -> Table)
Rất nhiều lập trình viên gặp lỗi permission denied for relation ... dù đã chạy lệnh GRANT SELECT ON ALL TABLES. Nguyên nhân là do chưa cấp quyền ở cấp độ Schema. Trong PostgreSQL, bạn phải mở khóa theo đúng thứ tự:
- Quyền kết nối vào Database:
SQL
GRANT CONNECT ON DATABASE ecommerce_db TO read_only_group, read_write_group;
-
Quyền truy cập Namespace (Schema):
Cần quyền
USAGEđể engine cho phép đi vào schema tìm kiếm các đối tượng bên trong:
SQL
GRANT USAGE ON SCHEMA orders TO read_only_group, read_write_group;
- Quyền trên các Bảng hiện có:
SQL
-- Cấp quyền đọc
GRANT SELECT ON ALL TABLES IN SCHEMA orders TO read_only_group;
-- Cấp quyền đọc-ghi cho nhóm write
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA orders TO read_write_group;
-- Đừng quên cấp quyền trên SEQUENCE nếu bảng dùng khóa tự tăng (BIGSERIAL / GENERATED ALWAYS AS IDENTITY)
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA orders TO read_write_group;
Bước 3: Cấu hình quyền mặc định cho các bảng trong tương lai (DEFAULT PRIVILEGES)
Các lệnh GRANT ở trên chỉ áp dụng cho những bảng đã tồn tại. Khi chạy migration tạo bảng mới, mặc định các role khác sẽ không có quyền truy cập. Cần cấu hình:
SQL
-- Đảm bảo bất kỳ bảng mới nào tạo ra trong schema orders bởi role dev_admin đều tự động cấp quyền:
ALTER DEFAULT PRIVILEGES IN SCHEMA orders
GRANT SELECT ON TABLES TO read_only_group;
ALTER DEFAULT PRIVILEGES IN SCHEMA orders
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO read_write_group;
ALTER DEFAULT PRIVILEGES IN SCHEMA orders
GRANT USAGE, SELECT ON SEQUENCES TO read_write_group;
3. Thu hồi quyền hạn (REVOKE)
Cú pháp thu hồi quyền tương tự như cấp quyền:
SQL
-- Thu hồi quyền DELETE từ nhóm ghi
REVOKE DELETE ON ALL TABLES IN SCHEMA orders FROM read_write_group;
-- Xóa user ra khỏi group role
REVOKE read_write_group FROM order_service_user;
Cảnh báo về Schema
public:Từ phiên bản PostgreSQL 15 trở đi, quyền tạo bảng trên schema
publicđã bị thu hồi khỏi mọi user ngoại trừ database owner. Tuy nhiên, trên các phiên bản cũ hơn (PG 14 trở về trước), bất kỳ ai có quyền connect đều có thể tạo bảng trongpublic. Lệnh bảo mật nên chạy:SQL
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
4. Tường lửa truy cập: Cấu hình file pg_hba.conf
Trước khi kiểm tra quyền trong SQL, PostgreSQL kiểm tra xem kết nối mạng đó có hợp lệ hay không thông qua file pg_hba.conf (HBA = Host-Based Authentication).
File này nằm trong thư mục dữ liệu $PGDATA.
Cấu trúc một dòng cấu hình trong pg_hba.conf:
Plaintext
# TYPE DATABASE USER ADDRESS METHOD
host ecommerce_db order_service 10.0.1.0/24 scram-sha-256
Bóc tách các trường:
-
TYPE(Loại kết nối):-
local: Kết nối cục bộ thông qua Unix Domain Socket (chỉ chạy trong nội bộ máy chủ). -
host: Kết nối TCP/IP thông thường (cả SSL lẫn không SSL). -
hostssl: Chỉ chấp nhận kết nối qua mạng nếu có mã hóa SSL/TLS. -
hostnossl: Chỉ chấp nhận kết nối không mã hóa.
-
-
DATABASE:-
Tên database cụ thể (ví dụ:
ecommerce_db). -
all: Áp dụng cho mọi database. -
@filename: Đọc danh sách database từ file ngoài.
-
-
USER:-
Tên role cụ thể (ví dụ:
order_service_user). -
all: Áp dụng cho mọi user. -
+group_role_name: Dấu+biểu thị áp dụng cho toàn bộ các role là thành viên của group này.
-
-
ADDRESS(Chỉ dùng chohost,hostssl,hostnossl):-
Dải IP mạng CIDR (ví dụ:
192.168.1.0/24,10.0.0.1/32). -
0.0.0.0/0: Chấp nhận mọi IP IPv4 (nguy hiểm nếu đưa ra internet). -
::/0: Chấp nhận mọi IP IPv6.
-
-
METHOD(Phương thức xác thực):-
scram-sha-256: Khuyến nghị chuẩn Production. Cơ chế băm mật khẩu bảo mật cao nhất hiện nay, chống nghe lén đường truyền. -
md5: Thuật toán băm cũ hơn (không còn được khuyến nghị cho môi trường mới). -
trust: Nguy hiểm. Cho phép đăng nhập không cần mật khẩu. Chỉ dùng cho môi trường dev cục bộ khép kín. -
reject: Chặn kết nối ngay lập tức. -
peer: Lấy username của hệ điều hành Linux map trực tiếp với username PostgreSQL (chỉ dùng cholocal).
-
File mẫu pg_hba.conf an toàn cho ứng dụng Production
Plaintext
# 1. Cho phép tài khoản quản trị postgres đăng nhập cục bộ qua Unix Socket
local all postgres peer
# 2. Cho phép các ứng dụng nội bộ trong subnet kết nối vào database nghiệp vụ bằng SCRAM-SHA-256
hostssl ecommerce_db +read_write_group 10.0.2.0/24 scram-sha-256
hostssl ecommerce_db +read_only_group 10.0.3.0/24 scram-sha-256
# 3. Cho phép replication user từ IP của Replica Server
hostssl replication rep_user 10.0.1.50/32 scram-sha-256
# 4. Mặc định chặn toàn bộ các kết nối còn lại
host all all 0.0.0.0/0 reject
Cơ chế khớp quy tắc (First-Match Rule):
PostgreSQL đọc
pg_hba.conftừ trên xuống dưới. Khi có một kết nối tới, nó sẽ áp dụng rule đầu tiên khớp với Type, Database, User và IP của client. Vì vậy, các quy tắc chi tiết, hạn chế phải đặt ở trên, quy tắc mặc định/chặn đặt ở dưới cùng.
Sau khi sửa file pg_hba.conf, bạn không cần khởi động lại toàn bộ PostgreSQL server mà chỉ cần reload:
SQL
SELECT pg_reload_conf();
Hoặc dùng lệnh terminal: docker exec -it postgres_lab pg_ctl reload.
5. Bảng tổng hợp kiểm tra quyền trong psql
Khi cần gỡ lỗi phân quyền trên server:
| Lệnh meta trong psql | Mục đích tra cứu |
|---|---|
\du hoặc \du+ |
Xem danh sách tất cả các Role, quyền hạn hệ thống và quan hệ thành viên (group membership). |
\dp <tablename> hoặc \z |
Xem ma trận quyền (Access Privileges) chi tiết trên từng bảng, cột, view. |
\dn+ |
Xem danh sách Schema kèm theo ai là chủ sở hữu (Owner) và quyền truy cập schema. |
Xem danh sách Schema kèm theo ai là chủ sở hữu (Owner) và quyền truy cập schema.
6. Tóm tắt & Bài tiếp theo
-
Role và User trong PostgreSQL là một; quản trị phân quyền nên dùng Group Role kết hợp
INHERIT. -
Để một User query được bảng, quyền phải được cấp đủ qua 3 tầng: Database (
CONNECT) -> Schema (USAGE) -> Object (SELECT/INSERT/UPDATE/DELETE). -
ALTER DEFAULT PRIVILEGESlà chìa khóa để tự động hóa phân quyền cho các bảng tạo trong tương lai. -
pg_hba.confđóng vai trò cổng gác mạng đầu tiên, hoạt động theo cơ chế first-match và nên cấu hình với phương thứcscram-sha-256.
Bài 6 xem tiếp: Hệ thống kiểu dữ liệu cơ bản: Numeric, String, Boolean, Date/Time và Timezone handling — chúng ta sẽ phân tích sâu các sai lầm kinh điển khi chọn kiểu dữ liệu (như
FLOATvsNUMERICtrong lưu trữ tiền tệ, lưu trữ mốc thời gian vớiTIMESTAMPvsTIMESTAMPTZ), và cách tối ưu hóa từng byte lưu trữ trên ổ đĩa.
All rights reserved