+3

Tích hợp Keycloak vào hệ thống ký số: Kiến trúc, Token và Phân quyền SIGNER

Trong hệ thống ký số, vấn đề lớn nhất không chỉ là việc kỹ thuật để ký (RSA/ECDSA, HSM, USB Token) mà còn có vấn đề về việc Ai là người được ký ? Chữ ký là của ai ? và có thể truy vết người kí hay không ?. Để giải quyết vấn đề, doanh nghiệp cần 1 hệ thống quản lý danh tính và phân quyền cho người dùng đủ mạnh, đủ an toàn và theo tiêu chuẩn OAuth2, OpenID Connect, SAML. Và Keycloak có thể giải quyết được những vấn đề này mà vẫn đảm bảo hệ thống đủ mạnh, đủ an toàn và hơn hết theo tiêu chuẩn. Vậy Keycloak là gì ? Nó tham gia như nào vào quy trình ký số ? Hệ thống backend của nó hoạt động như thế nào ? Bài viết này sẽ trả lời các câu hỏi đó đồng thời sẽ tóm lược toàn bộ quy trình tích hợp Keycloak vào hệ thống ký số, theo góc nhìn thực tế của một backend/API developer.

KeyCloak là gì ?

Keycloak là nền tảng quản lý đăng nhập (auth) và phân quyền (authorization) mã nguồn mở có tác dụng trong việc hỗ trợ đăng nhập, quản lý người dùng, sinh ra Token, bảo vệ API, hỗ trợ SSO và hơn hết đó là khả năng phân quyền người dùng. Keycloak thay thế toàn bộ hệ thống đăng nhập, JWT, refresh token, SSO, role-based access.

Keycloak ảnh hưởng gì tới hệ thống ký số?

Khi người dùng muốn ký số 1 tài liệu, họ phải chứng minh danh tính bản thân, lúc đó hệ thống sẽ phải giải quyết các vấn đề : Ai đang đăng nhập ? Người đó có được phép ký không ? Có thể truy vết được không ? Đăng nhập có hợp lệ không ? KeyCloak không kí file mà chỉ xác thực xem ai là người đăng nhập và người đó có quyền kí hay không. image.png

Kiến trúc tổng thể hệ thống ký số tích hợp Keycloak

image.png KEYCLOAK có vai trò quản lý user, phát hành JWT token, quản lý vai trò ký (SIGNER, APPROVER), kiểm soát session đăng nhập, lưu log đăng nhập. BACKEND API SERVER có vai trò nhận file từ client, kiểm tra token để xác thực user, kiểm tra quyền role SIGNER, hash file bằng SHA-256, ký hash (RSA/ECDSA), merge chữ ký, lưu log audit, trả file đã ký cho user. HSM / USB TOKEN là nơi chứa private key thật, đây là thứ mà KeyCloak dùng để kí vào Token nhằm mục đích đảm bảo chúng không bị sửa chữa. STORAGE (File Server + Database) là nơi để lưu trữ file gốc file đã ký, chữ ký rời (.sig) nếu dùng detached, audit (ai ký, lúc nào), certificate người ký, version của tài liệu

JWT Token trong Keycloak

Sau khi login, Keycloak tạo JWT - JSON Web Token- dạng: header.payload.signature mỗi phần được gọi là JSON và được base64-url encode.

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTYiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJtaW5oIiwicm9sZXMiOlsiU0lHTkVSIl19.QmVuX3RoYW5oX3RoeV9zYXUtY2h1Y2tfdGh1Y19jaHVuZw

Phần HEADER chứa thông tin về cách ký token

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "CqrSF...xyz"
}

Phần PAYLOAD chứa thông tin về user, role, thời gian, token,..

{
  "exp": 1732262400,
  "iat": 1732258800,
  "jti": "f4a9c62e-1234-5678-90ab-12f3c4d5e6f7",
  "iss": "https://keycloak.example.com/realms/myrealm",
  "aud": "account",

  "sub": "d8f4bb2a-1122-3344-5566-77889900aabb",
  "typ": "Bearer",
  "azp": "my-frontend-client",

  "preferred_username": "minh",
  "email": "minh@example.com",
  "email_verified": true,

  "realm_access": {
    "roles": [
      "offline_access",
      "SIGNER",
      "USER"
    ]
  },

Phần này được chia thành 5 phần chứa các thông tin tương ứng:

  • Nhóm thông tin thời gian : exp - expiration time (thời điểm token hết hạn), iat - issued at (thời điểm token được cấp)
  • Nhóm định danh token và issuer :jti - ID duy nhất của token (JWT ID), iss - issuer (người phát hành token), aud - audience (token dùng cho ai)
  • Nhóm user identity : sub - subject ID (ID nội bộ của user trong Keycloak), preferred_username - tên đăng nhập, email_verified - email đã xác minh chưa (true/false)
  • Nhóm client : azp - authorized party, typ - loại token
  • Nhóm role & permission : realm_access.roles - đây là role ở cấp realm, resource_access - đây là role gắn cho từng client

Phần SIGNATURE chứa thông tin về chữ ký để đảm bảo token không bị sửa

HMACSHA256(
  base64Url(header) + "." + base64Url(payload),
  privateKey
)

Quy trình kiểm tra token Keycloak bắt đầu từ backend nhận token từ header sau đó sẽ giải mã phần header và payload nhưng không cần key, vì chỉ base64. Tiếp theo Backend sẽ kiểm tra xem iss đúng realm không? exp hết hạn chưa? aud có khớp API không? Sau đó Backend lấy kid trong header từ đó sẽ tìm public key tương ứng trong danh sách JWK của Keycloak, từ đó sẽ dùng public key đó → verify chữ ký. Nếu verify OK thì sẽ tiếp tục đọc payload

Access Token – ID Token – Refresh Token

Loại token Vai trò Dùng ở đâu
Access Token JWT dùng để gọi API ký Backend
ID Token JWT dùng cho UI, hiển thị thông tin Frontend
Refresh Token Dùng để lấy token mới Keycloak

Access Token là quan trọng nhất bởi nhờ phần đó mà hệ thống mới có thể phân quyền ký

Protocol Mapper – Chìa khóa để thêm thông tin vào JWT

Trong hệ thống ký số, ta thường cần thêm nhiều thông tin hơn như Bộ phận: PhongKeToan, PhongNhanSu, Chức vụ: GiamDoc, TruongPhong, Mức ký: LEVEL_1, LEVEL_2, LEVEL_3 thế nhưng các thông tin này không nằm sẵn trong AccessToken và cũng không tự động xuất hiện. Lúc này, ta cần một cơ chế để lấy thông tin từ user trong Keycloak rồi bơm vào trong JWT Token để backend đọc được. Cơ chế đó chính là: Protocol Mapper. Các bước để thêm thông tin bằng Protocol Mapper:

  1. Bước 1: Thêm User Attribute trong Keycloak thêm: department = KeToan
  2. Bước 2: Tạo Protocol Mapper (User Attribute → claim): Clients → chọn client của backend (vd: sign-backend) → tab Mappers → Create
  3. Kết quả: Access Token sau này sẽ có thêm:
{
  ...
  "preferred_username": "minh",
  "realm_access": {
    "roles": ["SIGNER", "USER"]
  },
  "department": "KeToan",
}

Mapper không phải là “tính năng tự nghĩ của Keycloak”, mà nó dựa trên chuẩn OIDC + OAuth2. Cụ thể Token Keycloak phát hành là JWT theo chuẩn OIDC mà trong OIDC, người ta cho phép server trả thêm thông tin user Protocol Mapper chính là Keycloak implementation của cơ chế đó

Realm Role – Client Role – Group (Phân quyền trong hệ thống ký số)

Realm Role là vai trò chung toàn hệ thống bao gồm:

  • SIGNER
  • ADMIN
  • VIEWER

Trong Access Token, nó nằm trong:

"realm_access": {
  "roles": [
    "offline_access",
    "SIGNER",
    "USER"
  ]

Client Role (ROLESIGN) là vai trò gắn riêng cho một client cụ thể. Mỗi client sẽ có bộ role riêng, không đụng chạm client khác

Trong Access Token, nó nằm trong:

"resource_access": {
  "sign-backend": {
    "roles": [
      "ROLE_SIGN",
      "ROLE_VIEW_SIGN_HISTORY"
    ]
  },
  "report-service": {
    "roles": [
      "ROLE_VIEW_REPORT"
    ]
  }
}

Groups là “nhóm người dùng”, thường đại diện cho phòng ban / bộ phận. Mỗi Group có thể chứa nhiều user, các user được gán Realm Role và Client Role và có phân cấp. Thay vì phải gán role cho từng user thì bây giờ chỉ cần gán role cho group rồi thêm user vào đó group thì user được hưởng role từ group đó.

Nếu ta cấu hình thêm Group Mapper, token có thể có:

"groups": [
  "/PhongKeToan",
  "/ChiNhanhHaNoi"
]

Identity Brokering & User Federation

Khi triển khai thực tế nhiều nơi đã có sẵn hệ thống tài khoản, khi triển khai Keycloak, người ta không muốn tạo thêm 1 đống tài khoản mới, mật khẩu mới…. Lúc này sẽ cần sử dụng Identity Brokering và User Federation để giải quyết bài toán kết nối Keycloak với chỗ khác đang giữ danh tính / tài khoản.

Identity Brokering

Là việc Keycloak đóng vai “trung gian”, cho phép user login thông qua một Identity Provider khác (Google, Facebook, Azure AD, SSO của trường, …). Ta chỉ sử dụng khi muốn cho user login thông qua một Identity Provider khác hoặc muốn có Single Sign-On toàn tổ chức.

User Federation

Là việc Keycloak kết nối trực tiếp đến một “kho user” bên ngoài (thường là LDAP/Active Directory) để kiểm tra username/password. Khác với Identity Brokering, User Federation vẫn login trên form login của Keycloak, nhưng Keycloak đi hỏi LDAP/AD phía sau. Ta chỉ sử dụng khi công ty đã có LDAP/Active Directory và muốn dùng lại hệ thống user cũ, không phải nhập lại user sang Keycloak, không đổi hệ thống quản lý mật khẩu.

PKCE (Proof Key for Code Exchange)

Là cơ chế bảo mật bổ sung cho OAuth2 Authorization Code Flow. Dùng để ngăn kẻ tấn công đánh cắp Authorization Code, đông thời bảo vệ login trên Mobile App và SPA. Lý do cần PKCE là bởi OAuth2 bị một lỗ hổng lớn nếu hacker chặn được Authorization Code thì có thể chiếm quyền user. Trong SPA và mobile app code được truyền qua URL nên rất dễ bị lộ.

Những điều cần lưu ý khi triển khai thực tế

Khi triển khai Keycloak trong môi trường production (đặc biệt trong hệ thống ký số), mục tiêu là không bị downtime, không lộ token / cookie / mật khẩu, không lộ session, chạy bằng HTTPS, hỗ trợ nhiều user đồng thời, bảo mật ở mức cao nhất. Vì vậy, cần quan tâm đến Cluster, High Availability, Reverse Proxy, SSL/HTTPS, Cookie Security và CORS.

Cluster & High Availability (HA)

Cluster là việc nhiều instance Keycloak chạy song song cùng dùng chung 1 database. Còn HA là việc bảo đảm hệ thống còn sống dù 1 node bị chết.

keycloak-1 → http://10.0.0.11:8080
keycloak-2 → http://10.0.0.12:8080

Yêu cầu để cluster:

  • Tối thiểu 2 instance Keycloak
  • Dùng chung RDBMS (PostgreSQL, MariaDB…)
  • Có Load Balancer phía trước
  • Có thể dùng sticky session nếu muốn ổn định session SSO

Reverse Proxy (nginx)

Reverse Proxy là lớp đứng giữa Internet và Keycloak. Khi người dùng truy cập: https://auth.mycompany.com

Proxy chuyển tiếp:

→ http://keycloak-1:8080

→ http://keycloak-2:8080

Lý do cần Reverse Proxy:

  • Không để Keycloak lộ trực tiếp ra internet
  • Terminate SSL (đặt HTTPS ở proxy)
  • Load balancing
  • Thêm header forwarded
  • Chặn IP, giới hạn rate, thêm WAF

HTTPS

Trong hệ thống ký số, token giống như là hộ chiếu bởi vậy nếu truyền token qua HTTP, hacker có thể sniffing được từ đó có thể ký thay bạn. Còn khi sử dung HTTPS, Nginx nhận HTTPS rồi sẽ giải mã SSL rồi mới gửi HTTP nội bộ cho Keycloak (trong LAN) điều đó giúp cho bảo mật được tốt hơn.

Cookie Security

Keycloak dùng cookie để giữ session SSO. Cookie này phải được bảo vệ:

Flag Ý nghĩa
Secure Chỉ gửi cookie qua HTTPS
HttpOnly JS không đọc được cookie → chống XSS
SameSite Tránh CSRF

CORS (Cross-Origin Resource Sharing)

Là cơ chế của trình duyệt để chặn hoặc cho phép web front-end gọi API ở domain khác. Do frontend – backend – Keycloak thường khác domain

Frontend: https://portal-sign.com
Backend:  https://api-sign.com
Keycloak: https://auth.mycompany.com

Nên trình duyệt sẽ chặn request nếu CORS không được cấu hình đúng.

Trong Keycloak, mỗi client cần bật:Allowed CORS Origins = https://portal-sign.com

Trong Backend cũng cần:

Access-Control-Allow-Origin: https://portal-sign.com
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Allow-Methods: GET, POST, PUT, DELETE

Kết luận

Keycloak không phải công cụ ký số, nhưng nó là “cổng an ninh” của toàn bộ quy trình:

  • Xác thực người dùng
  • Phân quyền SIGNER
  • Tạo & verify token
  • SSO
  • Log đăng nhập
  • Quản lý phòng ban, cấp ký, chính sách

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí