Snowflake 7 — Bảo mật & phân quyền (RBAC, masking)

13 thg 7, 2026 2 lượt xem
#security
#rbac
#data-engineering
#snowflake
#data-masking

Vì sao bảo mật là bài toán khác trên Snowflake

Trong thế giới on-premise cũ, bảo mật dữ liệu ngân hàng phần lớn dựa vào hàng rào vật lý: kho dữ liệu nằm trong data center của NCB, ai truy cập được mạng nội bộ mới chạm tới được Oracle. Trên Snowflake, dữ liệu nằm trên hạ tầng cloud dùng chung, truy cập qua Internet — hàng rào vật lý biến mất. Bù lại, Snowflake xây một mô hình phân quyền khai báo (declarative) cực kỳ chi tiết, và nếu cấu hình đúng thì an toàn hơn nhiều so với "ai vào được mạng thì thấy hết".

Điểm mấu chốt: trên Snowflake, mọi thứ đều đi qua RBAC (Role-Based Access Control — kiểm soát truy cập dựa trên vai trò). Không có quyền nào gán trực tiếp cho user cả; quyền gán cho role, role gán cho user. Đây là khác biệt lớn so với PostgreSQL hay MySQL, nơi bạn có thể GRANT thẳng cho một user. Hiểu sai điều này là nguồn gốc của 90% sự cố "sao tôi có role mà vẫn không query được". Bài này mổ xẻ RBAC tới từng lớp, rồi đi qua các lớp bảo vệ cột (masking), lọc dòng (row access), che logic (secure view), phân loại dữ liệu (tagging) và biên giới mạng (network policy) — tất cả dưới góc nhìn che số CMND, số thẻ và số dư của khách hàng NCB.

Nếu bạn chưa nắm tổng quan kiến trúc Snowflake, xem lại Snowflake — Tổng quan. Chủ đề governance rộng hơn (data catalog, chất lượng dữ liệu) được bàn ở Governance & Data Quality.

RBAC: role → privilege → object

Ba khái niệm nền tảng cần phân biệt rạch ròi:

  • Privilege (đặc quyền): một hành động cụ thể trên một loại đối tượng — SELECT trên table, USAGE trên schema/warehouse, CREATE TABLE trong schema, OPERATE trên warehouse.
  • Role (vai trò): một túi chứa các privilege. User "mặc" role để lấy quyền.
  • Object (đối tượng có thể bảo mật): database, schema, table, view, warehouse, stage... mỗi cái đều có owner (chủ sở hữu là một role, không phải user).

Luồng gán quyền luôn theo hình chữ V: privilege được GRANT lên role, rồi role được GRANT xuống cho user (hoặc role khác). Một chi tiết cực kỳ hay bị bỏ sót: để query một bảng, không chỉ cần SELECT trên bảng, mà còn cần USAGE trên schema chứa nó USAGE trên database chứa schema đó. Thiếu một mắt xích trong chuỗi này là bảng "vô hình".

-- Minh hoạ (cú pháp Snowflake, không chạy trong sandbox)
-- Chuỗi quyền đầy đủ để analyst đọc bảng KHACHHANG
GRANT USAGE   ON DATABASE  CORE_BANK           TO ROLE ANALYST_RETAIL;
GRANT USAGE   ON SCHEMA    CORE_BANK.CUSTOMER  TO ROLE ANALYST_RETAIL;
GRANT SELECT  ON TABLE     CORE_BANK.CUSTOMER.KHACHHANG TO ROLE ANALYST_RETAIL;
GRANT USAGE   ON WAREHOUSE WH_ANALYST          TO ROLE ANALYST_RETAIL;

-- Gán role cho user cụ thể
GRANT ROLE ANALYST_RETAIL TO USER an_nguyen;

Còn một khái niệm hay nhầm: future grants. Nếu ngày mai team tạo thêm bảng mới trong schema CUSTOMER, analyst có tự động thấy không? Không — trừ khi bạn khai báo trước:

-- Minh hoạ: cấp SELECT cho MỌI bảng sẽ được tạo trong tương lai
GRANT SELECT ON FUTURE TABLES IN SCHEMA CORE_BANK.CUSTOMER TO ROLE ANALYST_RETAIL;

Role hierarchy: quyền chảy lên trên

Role có thể được gán cho role khác, tạo thành cây phân cấp. Quy tắc thừa kế: khi role A được gán cho role B (GRANT ROLE A TO ROLE B), thì B kế thừa mọi privilege của A. Quyền "chảy ngược lên" — role cha có tất cả quyền của role con. Vì vậy các role kỹ thuật cấp thấp (nhiều quyền chi tiết) nằm ở dưới, còn role quản trị nằm ở trên và tự động gom hết.

Sơ đồ trên đọc như sau: READONLY_BASE là role "nền" chỉ có quyền đọc cơ bản; mọi analyst đều kế thừa nó. SYSADMIN ngồi trên toàn bộ cây role tạo-đối-tượng, nên SYSADMIN thấy được mọi thứ mà các role kỹ thuật tạo ra — với điều kiện các role đó nằm dưới SYSADMIN trong cây.

Bốn role hệ thống và nguyên tắc phân tách

Snowflake tạo sẵn bốn role hệ thống, mỗi role có một sứ mệnh rõ ràng. Việc không lạm dụng chúng chính là xương sống của một tài khoản an toàn.

RoleVai tròNên/không nên
ACCOUNTADMINQuyền tối cao: billing, quản lý account, mọi thứ.Chỉ 2–3 người, bật MFA, KHÔNG dùng để chạy query hằng ngày.
SECURITYADMINQuản lý grant toàn cục, tạo/sửa role, quản lý network policy.Dành cho đội bảo mật; nắm quyền MANAGE GRANTS.
USERADMINTạo/xóa user và role (chỉ CRUD định danh, không gán quyền object).Tách khỏi người gán privilege để phân tách nhiệm vụ.
SYSADMINTạo và sở hữu database/warehouse/schema/table.Role "mẹ" của mọi role kỹ thuật; nơi đặt object.
PUBLICRole ngầm định mọi user đều có.KHÔNG bao giờ gán quyền nhạy cảm cho PUBLIC.

Nguyên tắc least privilege (quyền tối thiểu): mỗi role chỉ có đúng những quyền cần cho công việc, không thừa. Analyst bán lẻ không cần thấy schema rủi ro tín dụng; ETL loader cần INSERT nhưng không cần DELETE trên bảng lịch sử giao dịch.

Nguyên tắc ownership: role tạo ra object sẽ sở hữu nó và có toàn quyền, kể cả DROP. Best practice của Snowflake là không để SYSADMIN sở hữu trực tiếp mọi thứ một cách hỗn loạn, mà tạo một role sở hữu chung cho từng domain rồi gán role đó lên SYSADMIN. Điều này tránh tình huống một analyst vô tình sở hữu bảng production và có quyền xóa nó.

Một sai lầm kinh điển: gán ACCOUNTADMIN làm default role cho user để "cho tiện". Hậu quả là một câu DROP DATABASE gõ nhầm có thể xóa cả kho dữ liệu, và mọi hành động đều mang danh nghĩa quyền tối cao — không thể truy vết least privilege.

Dynamic Data Masking: che PII theo vai trò

Đây là tính năng đắt giá nhất cho ngân hàng. Masking policy là một hàm gắn vào cột; mỗi lần cột được query, Snowflake chạy hàm này để quyết định trả về giá trị thật hay giá trị đã che, dựa trên role của người query. Dữ liệu lưu trên đĩa vẫn nguyên vẹn (không hề bị sửa) — việc che diễn ra ngay lúc đọc (query time). Cùng một bảng, sếp bảo mật thấy số CMND đầy đủ, còn analyst chỉ thấy XXXXXX.

-- Minh hoạ (cú pháp Snowflake, không chạy trong sandbox)
-- Policy che số CMND: chỉ role COMPLIANCE thấy full, còn lại thấy che
CREATE MASKING POLICY mask_cmnd AS (val STRING) RETURNS STRING ->
  CASE
    WHEN CURRENT_ROLE() IN ('COMPLIANCE', 'ACCOUNTADMIN') THEN val
    ELSE 'XXXXXXXXX' || RIGHT(val, 3)   -- chỉ lộ 3 số cuối
  END;

-- Gắn policy vào cột
ALTER TABLE CORE_BANK.CUSTOMER.KHACHHANG
  MODIFY COLUMN so_cmnd SET MASKING POLICY mask_cmnd;

Vài điểm quan trọng khi triển khai:

  • Hàm CURRENT_ROLE() trả về role đang active trong phiên, không phải mọi role user có. Nếu logic cần xét toàn bộ role kế thừa, dùng IS_ROLE_IN_SESSION() để tính cả cây phân cấp.
  • Kiểu dữ liệu đầu vào/đầu ra của policy phải khớp cột. Che số dư (NUMBER) thì policy phải trả NUMBER — thường trả 0 hoặc NULL cho role không đủ quyền.
  • Một policy có thể gắn cho nhiều cột trên nhiều bảng, giúp quản lý tập trung: sửa logic che ở một nơi, áp dụng toàn hệ thống.

Với NCB, ta thường có ba mức: COMPLIANCE/FRAUD thấy full PII; ANALYST thấy che một phần (3 số cuối thẻ, số dư làm tròn theo bậc); và role BI/dashboard công khai thấy hoàn toàn NULL.

Row Access Policy: lọc dòng theo chi nhánh

Masking che cột; Row Access Policy (RAP) lọc dòng. Đây là cách thực thi quy tắc "chi nhánh Hà Nội chỉ thấy khách hàng của chi nhánh Hà Nội" ngay ở tầng dữ liệu, thay vì phó mặc cho từng dashboard tự lọc (dễ quên, dễ lách).

RAP là một hàm trả về BOOLEAN: dòng nào cho ra TRUE thì người query thấy, FALSE thì bị ẩn. Mẫu phổ biến nhất là mapping table — một bảng ánh xạ role/user tới phạm vi dữ liệu được phép.

-- Minh hoạ (cú pháp Snowflake, không chạy trong sandbox)
-- Bảng ánh xạ: role nào được xem chi nhánh nào
-- MAP_BRANCH(role_name, ma_chi_nhanh)

CREATE ROW ACCESS POLICY rap_branch AS (ma_cn STRING) RETURNS BOOLEAN ->
  CURRENT_ROLE() IN ('COMPLIANCE', 'ACCOUNTADMIN')   -- role tổng thấy tất cả
  OR EXISTS (
    SELECT 1 FROM CORE_BANK.SEC.MAP_BRANCH m
    WHERE m.role_name = CURRENT_ROLE()
      AND m.ma_chi_nhanh = ma_cn
  );

ALTER TABLE CORE_BANK.CUSTOMER.KHACHHANG
  ADD ROW ACCESS POLICY rap_branch ON (ma_chi_nhanh);

Cẩn trọng về hiệu năng: RAP chèn thêm điều kiện vào mọi query trên bảng, và nếu policy chứa subquery vào mapping table lớn thì mỗi lượt đọc phải join thêm. Nên giữ mapping table nhỏ, có clustering hợp lý, và tránh logic phức tạp trong policy. Kết hợp masking + RAP trên cùng một bảng là hoàn toàn được, và đó chính là cấu hình "phòng thủ hai lớp" chuẩn ngân hàng: chi nhánh chỉ thấy dòng của mình (RAP), và trong đó vẫn bị che PII nhạy cảm (masking).

Secure View & Secure UDF

View thường trên Snowflake có một lỗ hổng tinh vi: định nghĩa của nó có thể lộ ra qua metadata, và optimizer đôi khi "rò rỉ" dữ liệu qua thứ tự đánh giá điều kiện (một kẻ xấu có thể suy ra dữ liệu bị che bằng cách quan sát query nào lỗi, query nào không). Secure View (CREATE SECURE VIEW) khắc phục cả hai: giấu định nghĩa view khỏi người không sở hữu, và tắt các tối ưu hóa có thể rò rỉ dữ liệu.

-- Minh hoạ (cú pháp Snowflake, không chạy trong sandbox)
CREATE SECURE VIEW CORE_BANK.SHARE.V_KHACH_CONG_KHAI AS
  SELECT customer_id, city, product_group   -- chỉ trường an toàn
  FROM CORE_BANK.CUSTOMER.KHACHHANG;

Đánh đổi: secure view chạy chậm hơn view thường vì mất một số phép tối ưu. Chỉ dùng khi thật sự cần bảo mật — điển hình là view cấp cho đối tác bên ngoài qua data sharing (xem Snowflake — Time Travel & Data Sharing). Tương tự, Secure UDF giấu định nghĩa hàm; hữu ích khi logic tính điểm rủi ro hay công thức pricing là bí mật nghiệp vụ không được lộ cho bên nhận share.

Object tagging & tag-based masking

Khi có hàng nghìn cột trải trên hàng trăm bảng, gắn masking policy thủ công từng cột là không khả thi. Object tag giải quyết bài toán quy mô: gắn nhãn phân loại (ví dụ PII = 'HIGH') lên cột, rồi khai báo "mọi cột có tag này áp policy kia" — gọi là tag-based masking.

-- Minh hoạ (cú pháp Snowflake, không chạy trong sandbox)
CREATE TAG classification;

-- Gắn tag phân loại lên cột nhạy cảm
ALTER TABLE CORE_BANK.CUSTOMER.KHACHHANG
  MODIFY COLUMN so_the SET TAG classification = 'CARD_PAN';

-- Buộc mọi cột có tag CARD_PAN tự động chịu policy che thẻ
ALTER TAG classification SET MASKING POLICY mask_card;

Sức mạnh: bảng mới, cột mới, chỉ cần gắn đúng tag là tự động được bảo vệ, không phải nhớ gắn policy. Đây là nền tảng để scale governance ở quy mô ngân hàng, và tag còn phục vụ báo cáo tuân thủ ("liệt kê mọi nơi lưu số thẻ") — kết nối trực tiếp với công tác kiểm kê dữ liệu nhạy cảm trong Governance & Data Quality.

Network policy, mã hóa và audit

Network policy kiểm soát ở đâu được kết nối, độc lập với ai (RBAC) và thấy gì (masking). Nó là danh sách IP allow/block gắn ở cấp account hoặc user. Ngân hàng thường chỉ cho phép dải IP của văn phòng và VPN nội bộ, chặn mọi kết nối lạ ngay từ tầng mạng:

-- Minh hoạ (cú pháp Snowflake, không chạy trong sandbox)
CREATE NETWORK POLICY np_ncb_office
  ALLOWED_IP_LIST = ('203.0.113.0/24', '198.51.100.10');
ALTER ACCOUNT SET NETWORK_POLICY = np_ncb_office;

Mã hóa trên Snowflake là mặc định, end-to-end, không cần bật: dữ liệu mã hóa AES-256 khi lưu trữ (at rest) và TLS khi truyền (in transit). Khóa được quản lý theo phân cấp và tự động luân chuyển (key rotation) định kỳ. Ở gói Business Critical (thường bắt buộc cho ngân hàng), có thêm Tri-Secret Secure — kết hợp khóa của Snowflake với khóa do khách hàng tự quản (customer-managed key), cho phép NCB "thu hồi" quyền giải mã bằng cách vô hiệu khóa của mình.

Về audit, mọi hành động đều được ghi lại. Schema SNOWFLAKE.ACCOUNT_USAGE chứa các view như QUERY_HISTORY, ACCESS_HISTORY (ai đọc/ghi cột nào — cực quý cho điều tra rò rỉ), LOGIN_HISTORYGRANTS_TO_ROLES. Snowflake cũng cung cấp Trust Center — bảng điều khiển tập trung quét cấu hình account theo các scanner bảo mật (phát hiện user thiếu MFA, role gán quá rộng, network policy hở) và đưa ra khuyến nghị. Với công tác báo cáo phòng chống rửa tiền, chuỗi audit này là bằng chứng ai đã truy cập dữ liệu nào — liên quan chặt tới Phòng chống rửa tiền — Tổng quan.

Use case thực tế

Bối cảnh: NCB triển khai một bảng KHACHHANG (1,8 triệu dòng, 40 cột) trên Snowflake, phục vụ đồng thời ba nhóm: đội Compliance (12 người), analyst bán lẻ tại 25 chi nhánh (khoảng 90 người), và một dashboard công khai cho ban lãnh đạo. Yêu cầu tuân thủ: analyst chi nhánh chỉ được xem khách của chi nhánh mình; số CMND, số thẻ chỉ Compliance thấy full; số dư che theo bậc cho analyst.

Các bước triển khai:

  1. Thiết kế role: tạo COMPLIANCE, ANALYST_RETAIL (gán cho 90 user), BOARD_DASH; tất cả kế thừa READONLY_BASE. Cấp USAGE chuỗi database → schema → warehouse cho từng role.
  2. Masking policy: mask_cmndmask_card (Compliance full, còn lại 3 số cuối); mask_balance (Compliance full, analyst làm tròn về triệu, dashboard trả NULL). Gắn qua tag classification để mở rộng về sau.
  3. Row Access Policy: bảng MAP_BRANCH ánh xạ 25 role chi nhánh → mã chi nhánh; rap_branch cho Compliance thấy toàn bộ, analyst chỉ thấy chi nhánh mình.
  4. Network policy: chỉ cho dải IP văn phòng + VPN; user Compliance bắt buộc MFA.
  5. Kiểm chứng: đăng nhập giả lập bằng từng role, xác nhận analyst Hà Nội không thấy dòng của Đà Nẵng và số CMND hiện XXXXXXXXX456.

Kết quả đo được: một cấu hình duy nhất ở tầng dữ liệu thay thế cho logic lọc rải rác trong 30+ dashboard trước đây; chi phí bảo trì lọc quyền giảm mạnh vì bảng mới chỉ cần gắn tag là tự bảo vệ. Overhead hiệu năng do RAP + masking đo được khoảng 8–12% thời gian query trên bảng này — chấp nhận được đổi lấy đảm bảo tuân thủ. Khi kiểm toán nội bộ hỏi "ai đã xem số thẻ của khách X trong tháng qua", ACCESS_HISTORY trả lời trong vài phút thay vì dựng lại thủ công.

Ghi nhớ

  • Trên Snowflake không gán quyền trực tiếp cho user: privilege → role → user. Để query bảng cần cả USAGE (database + schema) lẫn SELECT (table).
  • Role hierarchy: role cha kế thừa mọi quyền của role con. Đặt role kỹ thuật dưới SYSADMIN, giữ ACCOUNTADMIN cho 2–3 người có MFA và không dùng hằng ngày.
  • Bốn role hệ thống phân tách nhiệm vụ: SYSADMIN sở hữu object, SECURITYADMIN quản grant, USERADMIN quản định danh, ACCOUNTADMIN quyền tối cao. Không gán quyền nhạy cảm cho PUBLIC.
  • Dynamic Data Masking che theo cột dựa trên CURRENT_ROLE(), thực hiện lúc query — dữ liệu gốc không đổi. Row Access Policy lọc theo dòng (mẫu mapping table cho phạm vi chi nhánh). Hai lớp kết hợp được.
  • Secure View/UDF giấu định nghĩa và chặn rò rỉ qua optimizer; dùng cho data sharing, đánh đổi hiệu năng.
  • Object tag + tag-based masking giúp scale: gắn nhãn phân loại là cột tự động chịu policy — bảng/cột mới được bảo vệ mà không cần nhớ gán.
  • Network policy kiểm soát IP; mã hóa AES-256 end-to-end mặc định, key tự luân chuyển; Business Critical có customer-managed key. Audit qua ACCOUNT_USAGE (ACCESS_HISTORY, QUERY_HISTORY) và Trust Center.

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