Quản trị dữ liệu 6 — Kiểm soát truy cập dữ liệu

13 thg 7, 2026 2 lượt xem
#rbac
#row-level-security
#data-engineering
#access-control
#abac

Quản trị dữ liệu 6 — Kiểm soát truy cập dữ liệu

Ba bài trước đã trả lời "dữ liệu là gì, đến từ đâu" (catalog & lineage), "có đúng không" (chất lượng) và "nhạy cảm đến mức nào" (phân loại & PII). Bài mã hóa & masking lo phần "giá trị bị che thế nào". Còn bài này trả lời câu hỏi cốt lõi nhất của kiểm toán: ai được nhìn thấy dữ liệu gì, và làm sao chứng minh điều đó đúng?

Kiểm soát truy cập (access control) không phải là bật vài quyền trong database rồi quên đi. Nó là một hệ thống gồm nguyên tắc, mô hình phân quyền, cơ chế thực thi ở nhiều tầng, và quy trình vận hành (cấp — thu hồi — rà soát lại). Làm đúng, một chi nhánh chỉ thấy khách của mình, một analyst không thấy được số CMND, và mỗi lần một DBA đọc bảng lương đều để lại dấu vết. Làm sai, dữ liệu Restricted rò rỉ qua một tài khoản dùng chung không ai nhớ ra.

Hai nguyên tắc nền: least privilege và need-to-know

Mọi thiết kế access control tốt đều bắt đầu từ hai nguyên tắc, và chúng khác nhau tinh tế:

  • Least privilege (đặc quyền tối thiểu): một chủ thể (người dùng, service account, job) chỉ được cấp đúng quyền cần để làm việc của mình, không hơn một milimet. Analyst cần đọc mart bán lẻ thì chỉ cấp SELECT trên schema đó — không cấp trên toàn database, không cấp DELETE, không cấp quyền admin "cho tiện". Đây là nguyên tắc về phạm vi quyền.
  • Need-to-know (chỉ biết khi cần): ngay cả khi ai đó có quyền kỹ thuật để truy cập, họ chỉ nên được thấy dữ liệu liên quan trực tiếp đến nhiệm vụ. Một nhân viên tín dụng ở chi nhánh Hà Nội có quyền đọc bảng khách hàng, nhưng nhu cầu công việc chỉ là khách hàng của chi nhánh mình — không phải toàn quốc. Đây là nguyên tắc về phạm vi dữ liệu.

Least privilege giới hạn có thể làm gì; need-to-know giới hạn được nhìn tới đâu. Column-level security (che cột) và row-level security (lọc dòng) chính là hai công cụ kỹ thuật để hiện thực hóa need-to-know, còn RBAC/ABAC là bộ máy để hiện thực hóa least privilege. Giữ hai nguyên tắc này trong đầu, mọi phần còn lại của bài sẽ mạch lạc.

RBAC vs ABAC: hai mô hình phân quyền

RBAC — Role-Based Access Control

RBAC gán quyền cho vai trò (role), rồi gán role cho người dùng. Người dùng không bao giờ nhận quyền trực tiếp — họ nhận role, và role mang theo tập quyền. Cấu trúc kinh điển: user → role → privilege → resource.

Ưu điểm là quản trị theo nhóm thay vì theo từng cá nhân. Ngân hàng có 200 giao dịch viên thì không cấp quyền 200 lần; ta tạo role teller, gán quyền cho role một lần, rồi mỗi giao dịch viên mới chỉ cần được thêm vào role. Khi nhân viên nghỉ việc, revoke role là mất sạch quyền. RBAC dễ hiểu, dễ audit ("ai có role gì" là một câu query), phù hợp với tổ chức có cấu trúc chức danh rõ ràng — như ngân hàng.

Nhược điểm là bùng nổ role (role explosion). Khi cần phân biệt theo nhiều chiều (chi nhánh × phòng ban × cấp bậc × loại sản phẩm), số role tổ hợp phình to: teller_hanoi_retail, teller_hcm_retail, teller_hanoi_corporate... Đến vài nghìn role thì bản thân việc quản lý role lại trở thành gánh nặng.

ABAC — Attribute-Based Access Control

ABAC không quyết định dựa trên role tĩnh mà dựa trên thuộc tính (attribute) được đánh giá tại thời điểm truy cập, theo một chính sách (policy) dạng luật. Bốn nhóm thuộc tính:

  • Subject (người truy cập): phòng ban, chi nhánh, cấp bậc, clearance level.
  • Resource (dữ liệu): nhãn nhạy cảm, chủ sở hữu, chi nhánh gắn với bản ghi.
  • Action: đọc, ghi, xuất.
  • Environment / context: giờ truy cập, địa chỉ IP, thiết bị, đang trong/ngoài mạng nội bộ.

Một policy ABAC điển hình: "Cho phép đọc bản ghi khách hàng NẾU subject.branch = resource.branchsubject.clearance >= resource.sensitivitytime trong giờ hành chính." Không cần tạo role riêng cho từng chi nhánh — luật tự so khớp thuộc tính. ABAC linh hoạt, xử lý được nhu cầu phân quyền tinh vi và động, và chính là nền tảng của row-level security hiện đại.

Nhược điểm: khó thiết kế, khó debug (khi bị từ chối truy cập, phải truy ngược policy engine để hiểu vì sao), và cần một hệ thống quản lý thuộc tính đáng tin cậy — nếu subject.branch sai thì cả chính sách sai.

Khi nào dùng cái nào

Tiêu chíRBACABAC
Cơ sở quyết địnhVai trò tĩnhThuộc tính động (subject/resource/env)
Độ linh hoạtThấp, quyền cố định theo roleCao, luật đánh giá lúc truy cập
Độ phức tạp vận hànhThấp, dễ auditCao, cần policy engine
Rủi roRole explosionPolicy khó debug, phụ thuộc chất lượng attribute
Hợp vớiQuyền theo chức danh, phân quyền thôRow-level, đa chiều, phụ thuộc ngữ cảnh

Thực tế ngân hàng thường kết hợp: RBAC làm khung thô cho quyền cấp-đối-tượng (role analyst_retail được SELECT trên schema mart_retail), còn ABAC/RLS làm lớp tinh để lọc bản ghi nào trong bảng đó mà người này được thấy. Đây là mô hình lai (hybrid) phổ biến nhất và cũng là điều các data warehouse hiện đại hỗ trợ sẵn.

Column-level security: che cột nhạy cảm theo vai trò

Column-level security (CLS) giới hạn cột nào một vai trò được thấy. Có hai cách:

  1. Ẩn hoàn toàn — vai trò không có quyền SELECT trên cột đó (một số DW hỗ trợ grant theo cột), hoặc cột bị loại khỏi VIEW cấp cho họ.
  2. Che một phần (masking động) — cột vẫn xuất hiện nhưng giá trị bị làm mờ theo vai trò: analyst thấy ****3456, còn nhân viên xử lý sự cố thấy đầy đủ.

Cách triển khai đơn giản và di động nhất là qua VIEW: tạo một view che sẵn các cột nhạy cảm rồi chỉ cấp quyền cho vai trò trên view đó, thu hồi quyền trên bảng gốc. Analyst query view, không bao giờ chạm bảng thật.

Dưới đây là định nghĩa view che số tài khoản (chỉ giữ 4 số cuối) mà DBA sẽ đặt vào một CREATE VIEW. Ở sandbox chỉ-đọc, ta chạy phần thân SELECT của nó để thấy kết quả che sẽ trông thế nào:

-- ▶ Chạy được
SELECT
  id,
  customer_id,
  regexp_replace(account_no, '.(?=.{4})', '*', 'g') AS account_no_masked,
  currency
FROM accounts
ORDER BY id;

Mẫu .(?=.{4})positive lookahead: khớp mọi ký tự mà phía sau nó còn ≥ 4 ký tự, tức là mọi ký tự trừ 4 ký tự cuối, rồi thay bằng *. Kết quả: 19012345678*******5678. Trong triển khai thật, DBA bọc chính câu này vào CREATE VIEW accounts_masked AS ..., GRANT SELECT ON accounts_masked TO analyst, và REVOKE SELECT ON accounts FROM analyst. Analyst thấy đủ cột để phân tích nhưng không lấy được số tài khoản đầy đủ. (Chi tiết kỹ thuật masking xem bài mã hóa & masking.)

Row-level security: chi nhánh chỉ thấy khách của mình

Row-level security (RLS) giới hạn dòng nào một chủ thể được thấy trong cùng một bảng. Đây là hiện thực trực tiếp của need-to-know: hai giao dịch viên query cùng SELECT * FROM customers, nhưng người ở chi nhánh Hà Nội chỉ nhận về khách Hà Nội, người ở TP.HCM chỉ nhận về khách TP.HCM — cùng một câu lệnh, kết quả khác nhau.

Về bản chất, RLS là một điều kiện WHERE được hệ thống tự động chèn dựa trên thuộc tính của người truy cập (ABAC ở tầng dòng). PostgreSQL có CREATE POLICY ... USING (...); Snowflake có row access policy; các DW khác có cơ chế tương đương. Điểm chung: predicate lọc thường so sánh một cột phân vùng của bảng (ví dụ city, branch_id) với một giá trị lấy từ ngữ cảnh phiên (session context) như CURRENT_USER hoặc một hàm trả về chi nhánh của người dùng.

Để minh họa hiệu ứng của RLS trên sandbox chỉ-đọc, ta mô phỏng predicate mà chính sách sẽ tự chèn — giả sử người đang truy cập thuộc chi nhánh phục vụ khách ở Hanoi và chỉ được xem tài khoản VND:

-- ▶ Chạy được
SELECT
  c.id,
  c.full_name,
  c.city,
  a.account_no,
  a.currency
FROM customers c
JOIN accounts a ON a.customer_id = c.id
WHERE c.city = 'Hanoi'
  AND a.currency = 'VND'
ORDER BY c.id;

Trong hệ thống thật, hai điều kiện WHERE này không do người dùng viết — họ chỉ chạy SELECT ... FROM customers JOIN accounts, và policy engine tự nối predicate c.city = <chi_nhánh_của_session> vào mọi truy vấn. Người dùng không thể "gỡ" điều kiện đó ra, kể cả khi họ viết WHERE 1=1, vì nó được áp ở tầng thấp hơn câu query. Đó là khác biệt cốt lõi giữa RLS thật và việc "tự nhớ thêm WHERE" — RLS không thể bị bỏ quên hay lách.

Lưu ý minh họa: hai block SQL trên là phần thân của secure view / mô phỏng predicate, không phải câu CREATE VIEW/CREATE POLICY (sandbox chỉ-đọc không chạy DDL). Chúng cho thấy kết quả mà cơ chế che cột và lọc dòng tạo ra.

Segregation of Duties: tránh xung đột quyền

Segregation of Duties (SoD) — phân tách nhiệm vụ — là nguyên tắc rằng không một cá nhân nào được nắm đủ quyền để tự mình hoàn thành một quy trình nhạy cảm và che giấu sai phạm. Đây là kiểm soát chống gian lận, khác với least privilege (chống lạm quyền do có quá nhiều quyền).

Ví dụ trong ngân hàng và trong đội dữ liệu:

  • Người tạo giao dịch không được là người phê duyệt nó (maker-checker).
  • Người cấp quyền truy cập không được là người phê duyệt yêu cầu cấp quyền đó.
  • DBA vận hành production không nên đồng thời là người audit log truy cập của chính mình — nếu không, ai canh người gác cổng?
  • Người viết pipeline nạp dữ liệu tài chính không nên đồng thời có quyền sửa trực tiếp số liệu trong mart đích mà không qua pipeline.

SoD được thực thi bằng cách thiết kế role sao cho các quyền xung đột không cùng nằm trong một role, và có một cơ chế phát hiện toxic combination — tổ hợp quyền nguy hiểm khi tập trung ở một người. Trước mỗi kỳ audit, đội governance chạy rà soát: "có ai vừa cấp được quyền vừa duyệt quyền không?" Nếu có, đó là một phát hiện (finding) phải xử lý.

Mô hình phân quyền trên Data Warehouse: grant qua role, không grant trực tiếp user

Đây là quy tắc vàng khi phân quyền trên DW (Snowflake, BigQuery, Redshift, Postgres...): luôn cấp quyền cho role, gán role cho user; tuyệt đối không cấp quyền trực tiếp cho user.

Lý do rất thực tế:

  • Audit gọn: muốn biết ai đọc được bảng lương, chỉ cần xem role nào có quyền đó và ai đang giữ role — thay vì quét quyền trực tiếp rải rác trên hàng trăm user.
  • Onboarding/offboarding sạch: nhân viên mới nhận role theo chức danh; nghỉ việc thì revoke role, không sót quyền "mồ côi" cấp trực tiếp từ hai năm trước.
  • Nhất quán: hai analyst cùng vai trò chắc chắn có cùng quyền vì cùng role, không lệch do ai đó từng cấp thêm tay.

Kiến trúc role phổ biến trên DW dùng role phân tầng (role hierarchy): role chức năng (theo quyền: reader_mart_retail, writer_staging) được gán vào role nghiệp vụ (theo chức danh: analyst_retail, engineer_ingest), rồi role nghiệp vụ gán cho user. Khi cần thêm quyền cho cả một nhóm chức danh, chỉ sửa role chức năng — user không đổi. Trên Snowflake đây chính là mô hình functional role kế thừa access role mà đội bảo mật khuyến nghị (xem thêm bảo mật & governance Snowflake).

Quy trình cấp, thu hồi và rà soát quyền

Kiểm soát truy cập không dừng ở kỹ thuật — nó cần một vòng đời quản trị rõ ràng, thường được đóng gói trong một hệ thống IAM/IGA (Identity Governance & Administration).

Các mắt xích quan trọng:

  • Access request: mọi yêu cầu quyền đều qua form có ghi ai xin, xin quyền gì, lý do nghiệp vụ, phạm vi, thời hạn. Không cấp quyền qua tin nhắn miệng.
  • Phê duyệt nhiều cấp: thường ít nhất quản lý trực tiếp (xác nhận need-to-know) và data owner (xác nhận cho phép chạm dữ liệu này). Với dữ liệu Restricted, thêm cấp bảo mật.
  • Recertification (rà soát tái cấp) định kỳ: mỗi quý hoặc 6 tháng, quản lý và data owner phải chủ động xác nhận lại rằng từng người vẫn cần quyền đang giữ. Quyền không được xác nhận sẽ bị thu hồi tự động. Đây là vũ khí chống privilege creep — hiện tượng quyền tích tụ dần khi người ta đổi vị trí mà quyền cũ không bị gỡ.
  • Thu hồi (revoke) tức thời: nghỉ việc, chuyển bộ phận, hoặc phát hiện lạm dụng → revoke ngay, không chờ kỳ recert.

Privileged Access Management (PAM)

Tài khoản đặc quyền — admin DW, DBA production, service account có quyền rộng — là mục tiêu tấn công số một và cần kiểm soát riêng bằng PAM:

  • Không dùng chung tài khoản admin; mỗi hành động phải quy được về một con người.
  • Just-in-time (JIT) access: quyền admin được cấp tạm thời khi cần, tự hết hạn sau vài giờ, thay vì gán vĩnh viễn (standing privilege).
  • Session recording + phê duyệt: phiên truy cập đặc quyền được ghi lại và có thể yêu cầu người thứ hai duyệt (four-eyes) cho thao tác nhạy cảm.
  • Vault mật khẩu/khóa: credential đặc quyền lưu trong vault, xoay vòng (rotate) định kỳ.

Audit truy cập: chứng minh mọi thứ đúng

Kiểm soát nào cũng vô nghĩa nếu không chứng minh được nó hoạt động. Audit log truy cập ghi lại ai, khi nào, truy cập gì, hành động gì, thành công hay bị từ chối. Với dữ liệu Restricted, mỗi lần đọc phải để lại dấu vết.

Yêu cầu chất lượng audit log: bất biến (immutable) — không ai, kể cả DBA, sửa được log; tập trung — gom về một nơi tách khỏi hệ thống bị giám sát (SoD với chính người vận hành); đủ chi tiết để tái dựng sự việc; lưu đủ lâu theo quy định (thường nhiều năm với ngành ngân hàng). Log này còn feed sang SIEM để phát hiện bất thường (một analyst đột nhiên đọc 50.000 bản ghi khách trong đêm là tín hiệu cần điều tra). Chi tiết vòng đời và audit xem bài lifecycle & audit; mối liên hệ với giám sát giao dịch đáng ngờ xem phòng chống rửa tiền.

Use case thực tế

Bối cảnh: Kho phân tích của NCB có bảng dim_customer (2,4 triệu bản ghi) và fact_transaction. Trước đây, toàn bộ 45 analyst chi nhánh dùng chung một role analyst_all với SELECT trên toàn bộ mart — nghĩa là một analyst chi nhánh Đà Nẵng đọc được khách VIP ở Hà Nội, và số CMND hiển thị đầy đủ. Kiểm toán nội bộ chấm đây là finding mức cao: vi phạm cả need-to-know lẫn phân loại Restricted.

Xử lý theo bài này:

  1. Tái cấu trúc role (RBAC). Bỏ analyst_all. Tạo role chức năng reader_mart_retail (SELECT trên view đã che cột), gán vào role nghiệp vụ analyst_branch. Không user nào còn quyền trực tiếp trên bảng gốc.
  2. Column-level. Tạo view customers_masked che national_id (chỉ 4 số cuối) và account_no bằng regexp_replace như block SQL ở trên. Chỉ 3 nhân viên xử lý sự cố giữ role pii_full thấy giá trị đầy đủ, và mọi lần đọc của họ đều bị audit.
  3. Row-level (ABAC). Áp row access policy: predicate dim_customer.branch_id = user_branch() tự chèn vào mọi query, với user_branch() lấy chi nhánh từ context phiên. Sau khi bật, một analyst Đà Nẵng chạy SELECT COUNT(*) FROM dim_customer chỉ còn thấy ~180.000 bản ghi thay vì 2,4 triệu.
  4. SoD & PAM. Tách quyền cấp-role khỏi quyền duyệt-request; chuyển 2 tài khoản DBA dùng chung sang JIT access qua vault.
  5. Recertification. Đưa 45 analyst vào chu kỳ rà soát quý; ngay kỳ đầu phát hiện 6 người đã chuyển bộ phận nhưng còn giữ quyền cũ → revoke.

Kết quả: finding được đóng, phạm vi dữ liệu mỗi analyst nhìn thấy giảm hơn 90%, và mọi truy cập PII đầy đủ đều truy vết được về một con người cụ thể.

Ghi nhớ

  • Least privilege giới hạn quyền làm gì; need-to-know giới hạn dữ liệu nhìn tới đâu — hai nguyên tắc bổ trợ, không thay thế nhau.
  • RBAC gán quyền theo role (đơn giản, dễ audit, dễ role explosion); ABAC quyết định theo thuộc tính động (linh hoạt, khó debug). Ngân hàng thường lai: RBAC cấp-đối-tượng + ABAC/RLS cấp-dòng.
  • Column-level security che/ẩn cột nhạy cảm theo vai trò (thường qua secure VIEW); row-level security lọc dòng theo ngữ cảnh người truy cập — predicate do hệ thống tự chèn, người dùng không thể gỡ.
  • Segregation of Duties ngăn một người nắm đủ quyền để gian lận và che giấu (maker-checker, tách cấp quyền khỏi duyệt quyền, tách vận hành khỏi audit).
  • Trên DW: grant qua role, không grant trực tiếp user; dùng role hierarchy (role chức năng → role nghiệp vụ → user) để audit và onboarding/offboarding sạch.
  • Quy trình quyền là vòng đời: request → phê duyệt nhiều cấp → grant có log → recertification định kỳ → revoke. Recert chống privilege creep; PAM (JIT, vault, session recording) kiểm soát tài khoản đặc quyền.
  • Audit log phải bất biến, tập trung, đủ chi tiết và lưu đủ lâu — không chứng minh được thì kiểm soát coi như không tồn tại.

Bài viết liên quan

CSV/JSON/Parquet/Avro, lưu trữ theo hàng vs cột, nén, và OLTP vs OLAP.

13 thg 7, 2026 8

Data Engineering là gì, vai trò trong vòng đời dữ liệu, và bức tranh hệ sinh thái công cụ.

13 thg 7, 2026 5

Vì sao xử lý phân tán, mô hình Spark (RDD/DataFrame), lazy evaluation, shuffle và tối ưu.

13 thg 7, 2026 5

Đảm bảo chất lượng & minh bạch dữ liệu bằng dbt: data tests dựng sẵn (unique, not_null, relationships, accepted_values), singular test, unit test, và tài liệu tự sinh kèm lineage graph.

13 thg 7, 2026 5