Dạo này blog thiếu các bài về kỹ thuật nên mình dự định làm một loạt bài về bảo mật (security) nhằm chia sẻ với các bạn một số góc nhìn của mình về bảo mật khi triển khai các hệ thống web. Mình sẽ không đề cập nhiều đến code bởi bảo mật là vấn đề kiến trúc và thiết kế hệ thống nên mọi người muốn triển khai hay áp dụng thế nào thì tùy trình của mỗi người.
Bài này mình sẽ nói về mô hình AAA (Authentication, Authorization và Accounting). Đây là một mô hình mà mình đã đọc được từ khi học năm I, lúc còn lăng xăng mấy cái course CCNA, Security+ và Network+. Các khóa học (hay sách ^^) này đều đề cập đến mô hình 3A này. Từ đó đến giờ, mình luôn áp dụng mô hình này để xây dựng hệ thống liên quan đến phân quyền.
1. Authentication
Mình tạm dịch Authentication là xác thực, chỉ quá trình định danh (hay xác định) một tài khoản đang vào hệ thống chính là người đó chứ không phải ai khác. Đây là bước ban đầu của mọi hệ thống có yếu tố người dùng. Nếu không có bước xác thực này, hệ thống của bạn sẽ không biết được người đang truy cập vào hệ thống là ai để có các phản hồi phù hợp.
Bước xác thực này rất quen thuộc và thông dụng, thường biểu hiện ở hình thức đơn giản nhất chính là form đăng nhập vào hệ thống. Đây là mô hình xác thực dựa trên yếu tố “mật khẩu”. Mật khẩu là một trong những phương pháp được triển khai cho hệ thống xác thực (authentication). Một số phương thức xác thực thông dụng khác là khóa (public & private), sinh học (vân tay, tròng mắt, khuôn mặt)…
1.1 Mật khẩu (Password & Pin)
Mật khẩu là một trong những phương pháp đơn giãn và dễ triển khai nhất. Thường mỗi hệ thống sẽ lưu lại mật khẩu ở dạng đã được mã hóa một chiều (md5, sha1, tự chế…) để đảm bảo mật khẩu có bị lộ cũng không thể khôi phục thành chuỗi gốc.
Phương pháp này còn có nhiều biến thể như thiết kế dạng Swipe Pattern PIN (trong các điện thoại android) hoặc mật khẩu dùng một lần (dùng cho các chức năng quan trọng).
1.2 Khóa (Public-key cryptography)
Phương pháp này dựa trên thuật toán mã hóa khóa công cộng (public key) và khóa cá nhân (private key). Phương pháp này giúp cho người đăng nhập không cần nhớ thông tin gì về đăng nhập như phương pháp mật khẩu. Để đăng nhập vào hệ thống, bạn chỉ cần có khóa cá nhân (private key) trên máy và đăng nhập vào hệ thống (nếu đã khai báo với khóa công cộng của bạn). Cách này thường được áp dụng và bật với các hệ thống quản trị server. Mình sẽ viết một bài về khóa công cộng sau ^^.
1.3 Sinh học (Biometrics)
Đây là phương pháp dựa trên các yếu tố đặc trưng bởi người dùng như dấu vân tay, tròng mắt hoặc khuôn mặt. Phương pháp này có cái hay là bạn luôn “vác” cái “mật khẩu trời sinh” theo bên người mà không cần nhớ và chỉ dùng nó mỗi khi cần đăng nhập vào hệ thống.
Như vậy, có nhiều phương pháp để xác thực một tài khoản. Mỗi phương pháp đều có điểm mạnh nhưng cũng ẩn chứa những rủi ro khi triển khai như mất mật khẩu, mất khóa cá nhân, bị đánh cắp vân tay…Nếu trên web, phương thức mật khẩu có vẻ là dễ triển khai và có nhiều lợi thế hơn vì thường thao tác trên màn hình và độ chính xác cao. Chúng ta có thể cải tiến hệ thống bảo mật hơn như theo dõi thói quen đăng nhập, địa điểm, IP, trình duyệt, mật khẩu một lần…
2. Authorization
Sau khi xác định được “danh tính” của tài khoản thì hệ thống mới chỉ trả lời được câu hỏi “Đó là ai?”, chúng ta sẽ tiến hành một bước quan trọng không kém đó là trả lời câu hỏi “Người đó có thể làm được gì?”, hay xác định quyền (phân quyền) của tài khoản hiện tại vừa mới được xác thực.
Hệ thống của bạn có thể sẽ rất phức tạp bởi nhiều tính năng quan trọng và mạng lưới phòng ban dày đặc và cần phân chia quyền sử dụng rõ ràng nên việc thiết kế một hệ thống phân quyền cho từng thành viên là một việc làm cực kỳ quan trọng và cần thiết.
Để hệ thống “phân quyền” vận hành hoàn chỉnh, theo kinh nghiệm của mình thì bạn cần thiết kế hai thành phần riêng là phân quyền theo nhóm (Role-based) và phân quyền theo đối tượng (tài khoản…) cụ thể (Object-based).
2.1 Role-based authorization
Đây là hướng tiếp cận đơn giản nhất cho việc phân quyền. Bạn đặt mỗi tài khoản vào một nhóm nhất định (vd: Administrator, Moderator, Member, Guest…) và tiến hành phân quyền cho từng nhóm này. Hướng tiếp cận này sẽ giúp bạn tiết kiệm được rất nhiều thời gian cho vấn đề phân quyền khi hệ thống của bạn có nhiều tài khoản và mọi thứ sẽ tự động.
Nếu hệ thống của bạn thiết kế theo mô hình MVC (hoặc Group-MVC) thì bạn có thể phân quyền của từng nhóm đến từng Action trong các Controller. VD: Nhóm Moderator có thể vào Action Xóa sản phẩm của Controller Sản phẩm thuộc Group Controller CMS…hoặc có thể diễn dịch trong code theo một số dạng sau cho từng nhóm:
…
[ROLE]: /{group controller}/{controller}/{action} GROUP_MODERATOR: /cms/product/delete GROUP_MODERATOR: /erp/log/* GROUP_ADMINISTRATOR: /cms/* GROUP_ADMINISTRATOR: /erp/* ...
Nếu hệ thống của bạn cần những chức năng phân quyền cao hơn như chỉ có ông A mới xem được trang này, ông B mới xóa được cái kia, bà C mới duyệt được cái nọ…thì hệ thống role-based không thể đáp ứng được, bạn phải thiết kế thêm một lớp phân quyền nữa là object-based.
2.2 Object-based authorization
Kiến trúc phân quyền này sẽ giúp phân quyền được đến từng tài khoản cụ thể hoặc giải quyết các bài toán phân quyền như những người trong nhóm A,B có thể vào /cms/product/edit (trang edit sản phẩm) nhưng người nhóm A chỉ được edit sản phẩm thuộc danh mục X, còn người nhóm B chỉ được edit sản phẩm thuộc danh mục Y…
Kiến trúc object-based lý tưởng phải được khai báo động trong database và có thêm lớp cache để giải quyết vấn đề performance khi check quyền. Bởi chức năng phân quyền này là custom cho từng đối tượng cụ thể và từng chức năng cụ thể, chúng ta có thể nhúng đoạn code phân quyền vào từng Action cần kiểm tra quyền hoặc triển khai ở tầng cao hơn để check chung cho toàn bộ các chức năng. Tùy theo performance và hệ thống object-based của bạn có phức tạp hay không mà đưa ra quyết định nơi sẽ đặt đoạn code kiểm tra phân quyền.
Định dạng phân quyền có thể tóm tắt như sau:
{type[DEPARTMENT|ACCOUNT]}:{rid}:{objttype[CONTROLLER|PRODUCT]} : {objinfo} ROLETYPE_DEPARTMENT : DEPARTMENT_SALE : OBJECTTYPE_POSM : * ROLETYPE_ACCOUNT: 1 : OBJECTTYPE_CONTROLLER : /cms/log ROLETYPE_ACCOUNT: 1234 : OBJECTTYPE_REPORT : MONTH_TOTAL_SALE ...
Như vậy, bạn có thể triển khai việc kiểm tra quyền của một tài khoản theo 1 trong 2 cách (hoặc cả 2 cách) để có thể phân quyền đầy đủ cho toàn bộ hệ thống của mình.
3. Accounting (hay Auditing)
Quá trình cuối cùng của hệ thống phân quyền gọi là Accounting (hay còn gọi là Auditing), mình tạm dịch là kiểm tra hay ghi log. Quá trình kiểm tra là công đoạn ghi lại các hành động của người sau khi đã thực hiện một chức năng nào đó trong hệ thống.
Tùy theo nhu cầu kiểm tra (ghi log) mà bạn sẽ ra quyết định nên ghi lại những hành động nào của user hoặc có thể ghi lại hết nếu hệ thống của bạn yêu cầu như vậy. Việc ghi log là khá nặng nếu bạn triển khai cho toàn bộ hệ thống, bởi mỗi tài khoản khi vào hệ thống, họ có thể thao tác khá nhiều trên website, mỗi thao tác đều được ghi lại sẽ khiến cho hệ thống log của bạn ngày càng phình to, nếu không có kế hoạch và phân tích rõ ràng thì việc ghi log nhiều khi sẽ không đem lại tác dụng nhiều và thông tin trong log rất dễ bị nhiễu.
Việc ghi log có hai tác dụng rõ ràng là đánh giá, theo dõi hoạt động của thành viên (nhân viên…) trên hệ thống và điều tra khi có sự cố mất mác, sai lệch, rò rĩ thông tin mà không rõ nguyên nhân. Nếu được thiết kế tốt, hệ thống log sẽ giúp cho mọi hoạt động của hệ thống bạn được rõ ràng, minh bạch và an toàn.
Một hệ thống log có thể được thiết kế theo dạng:
{accid}:{type[PRODUCT|...]}:{oid}:{action}:{moreinfo}:{ip}:{time} 101 : TYPE_PRODUCT : 54321 : ADD : "" : 1.2.3.4 : 12:34 08/08/2013 654 : TYPE_PRODUCT : 54321 : EDIT : "" : 4.3.2.1 : 23:09 12/08/2013 ...
——
Như vậy, mình đã giới thiệu với mọi người làm thế nào để xây dựng được một hệ thống user (phân quyền) để đáp ứng được nhu cầu cho hệ thống online của các bạn. Bài viết dựa trên kinh nghiệm mà mình đã triển khai cho framework Litpi (https://github.com/voduytuan/litpi) của mình cũng như website dienmay.com mà mình đang làm việc và nghiên cứu.