Các giải pháp lập trình C# phần 9

Cơchế RBScủa .NET Frameworktrừu tượng hóa các tính năng bảo mật dựa-trên-người-dùng

của hệ điều hành nằm dưới thông qua hai giao diện chính sau đây:

• System.Security.Principal.IIdentity

• System.Security.Principal.IPrincipal

Giao diện IIdentitymô tảthực thểmà mã lệnh hiện đang chạy trên danh nghĩa của thực thể

này, chẳng hạn một người dùng hoặc tài khoản dịch vụ (service account). Giao diện

IPrincipalmô tả IIdentitycủa thực thểvà tập các vai trò mà thực thểnày thuộc về. Một vai

trò chỉlà một sựphân loại, dùng đểnhóm các thực thểvới các khảnăng bảo mật tương tự

nhau, chẳng hạn một nhóm người dùng Windows.

Đểtích hợp RBSvới bảo mật người dùng Windows, .NET Frameworkcung cấp hai lớp sau

đây (hiện thực giao diện IIdentityvà IPrincipal):

• System.Security.Principal.WindowsIdentity

• System.Security.Principal.WindowsPrincipal

Lớp WindowsIdentityhiện thực giao diện IIdentityvà mô tảmột người dùng Windows. Lớp

WindowsPrincipalhiện thực IPrincipalvà mô tảtập các nhóm Windows mà người dùng này

thuộc về. Vì .NET RBSlà một giải pháp chung được thiết kếsao cho độc lập nền, bạn không

thểtruy xuất các tính năng và các khảnăng của tài khoản người dùng Windows thông qua

giao diện IIdentity và IPrincipal, bạn phải sử dụng trực tiếp các đối tượng

WindowsIdentityvà WindowsPrincipal.

Đểxác định người dùng hiện hành có phải là thành viên của một nhóm Windows nào đó hay

không, trước tiên bạn phải gọi phương thức tĩnh WindowsIdentity.GetCurrent. Phương thức

này trả về một đối tượng WindowsIdentity mô tả người dùng Windows mà tiểu trình hiện

đang chạy trên danh nghĩa của người dùng này. Kếtiếp, tạo một đối tượng WindowsPrincipal

mới và truyền đối tượng WindowsIdentity cho phương thức khởi dựng. Cuối cùng, gọi

phương thức IsInRolecủa đối tượng WindowsPrincipal đểkiểm tra xem người dùng này có

nằm trong một nhóm (vai trò) cụthểnào đó hay không. IsInRoletrảvề truenếu người dùng

này là thành viên của nhóm đã được chỉ định, ngược lại trảvề false.

pdf53 trang | Chia sẻ: oanh_nt | Lượt xem: 1364 | Lượt tải: 2download
Bạn đang xem trước 20 trang nội dung tài liệu Các giải pháp lập trình C# phần 9, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
424 Chương 13: Bảo mật 13.13 Xác định người dùng hiện hành có là thành viên của một nhóm Windows nào đó hay không  Bạn cần xác định người dùng hiện hành của ứng dụng có phải là thành viên của một nhóm người dùng Windows nào đó hay không.  Thu lấy đối tượng System.Security.Principal.WindowsIdentity mô tả người dùng hiện hành bằng phương thức tĩnh WindowsIdentity.GetCurrent. Kế tiếp, truyền đối tượng WindowsIdentity cho phương thức khởi dựng của lớp System.Security.Principal.WindowsPrincipal để thu lấy đối tượng WindowsPrincipal. Cuối cùng, gọi phương thức IsInRole của đối tượng WindowsPrincipal để xác định người dùng này có nằm trong một nhóm Windows nào đó hay không. Cơ chế RBS của .NET Framework trừu tượng hóa các tính năng bảo mật dựa-trên-người-dùng của hệ điều hành nằm dưới thông qua hai giao diện chính sau đây: • System.Security.Principal.IIdentity • System.Security.Principal.IPrincipal Giao diện IIdentity mô tả thực thể mà mã lệnh hiện đang chạy trên danh nghĩa của thực thể này, chẳng hạn một người dùng hoặc tài khoản dịch vụ (service account). Giao diện IPrincipal mô tả IIdentity của thực thể và tập các vai trò mà thực thể này thuộc về. Một vai trò chỉ là một sự phân loại, dùng để nhóm các thực thể với các khả năng bảo mật tương tự nhau, chẳng hạn một nhóm người dùng Windows. Để tích hợp RBS với bảo mật người dùng Windows, .NET Framework cung cấp hai lớp sau đây (hiện thực giao diện IIdentity và IPrincipal): • System.Security.Principal.WindowsIdentity • System.Security.Principal.WindowsPrincipal Lớp WindowsIdentity hiện thực giao diện IIdentity và mô tả một người dùng Windows. Lớp WindowsPrincipal hiện thực IPrincipal và mô tả tập các nhóm Windows mà người dùng này thuộc về. Vì .NET RBS là một giải pháp chung được thiết kế sao cho độc lập nền, bạn không thể truy xuất các tính năng và các khả năng của tài khoản người dùng Windows thông qua giao diện IIdentity và IPrincipal, bạn phải sử dụng trực tiếp các đối tượng WindowsIdentity và WindowsPrincipal. Để xác định người dùng hiện hành có phải là thành viên của một nhóm Windows nào đó hay không, trước tiên bạn phải gọi phương thức tĩnh WindowsIdentity.GetCurrent. Phương thức này trả về một đối tượng WindowsIdentity mô tả người dùng Windows mà tiểu trình hiện đang chạy trên danh nghĩa của người dùng này. Kế tiếp, tạo một đối tượng WindowsPrincipal mới và truyền đối tượng WindowsIdentity cho phương thức khởi dựng. Cuối cùng, gọi phương thức IsInRole của đối tượng WindowsPrincipal để kiểm tra xem người dùng này có nằm trong một nhóm (vai trò) cụ thể nào đó hay không. IsInRole trả về true nếu người dùng này là thành viên của nhóm đã được chỉ định, ngược lại trả về false. 425 Chương 13: Bảo mật  Bạn có thể thu lấy tham chiếu IPrincipal đến một đối tượng WindowsPrincipal bằng thuộc tính tĩnh CurrentPrincipal của lớp System.Threading.Thread. Tuy nhiên, kỹ thuật này tùy thuộc vào cấu hình chính sách principal của miền ứng dụng hiện hành; mục 13.14 sẽ thảo luận vấn đề này chi tiết hơn. Phương thức IsInRole có ba phiên bản nạp chồng: • Phiên bản thứ nhất nhận một chuỗi chứa tên của nhóm cần kiểm tra. Tên nhóm phải có dạng [DomainName]\[GroupName] đối với các nhóm dựa-trên-miền và phải có dạng [MachineName]\[GroupName] đối với các nhóm được định nghĩa cục bộ. Nếu muốn kiểm tra tư cách thành viên của một nhóm Windows chuẩn, bạn hãy sử dụng dạng BUILTIN\[GroupName]. IsInRole thực hiện kiểm tra có phân biệt chữ hoa-thường đối với tên nhóm được chỉ định. • Phiên bản thứ hai nhận một số nguyên (int), số này chỉ định một Windows Role Identifier (RID). RID cung cấp một cơ chế để nhận biết các nhóm, không phụ thuộc vào ngôn ngữ (language) và sự bản địa hóa (localization). • Phiên bản thứ ba nhận một thành viên thuộc kiểu liệt kê System.Security.Principal.WindowsBuiltInRole. Kiểu liệt kê này định nghĩa các thành viên mô tả các nhóm Windows có sẵn. Bảng 13.3 liệt kê tên, RID, và giá trị WindowsBuiltInRole cho mỗi nhóm Windows chuẩn. Bảng 13.3 Tên, RID, và giá trị WindowsBuiltInRole của các tài khoản có sẵn Tên tài khoản RID (Hex) Giá trị WindowsBuiltInRole BUILTIN\Account Operators 0x224 AccountOperator BUILTIN\Administrators 0x220 Administrator BUILTIN\Backup Operators 0x227 BackupOperator BUILTIN\Guests 0x222 Guest BUILTIN\Power Users 0x223 PowerUser BUILTIN\Print Operators 0x226 PrintOperator BUILTIN\Replicators 0x228 Replicator BUILTIN\Server Operators 0x225 SystemOperator BUILTIN\Users 0x221 User [  Lớp WindowsIdentity cung cấp các phương thức khởi dựng nạp chồng cho phép bạn thu lấy đối tượng WindowsIdentity mô tả một người dùng nào đó (khi chạy trên Microsoft Windows Server 2003 trở về sau). Bạn có thể sử dụng đối tượng này và phương pháp được mô tả trong mục này để xác định xem người dùng đó có phải là thành viên của một nhóm Windows nào đó hay không. 426 Chương 13: Bảo mật Nếu bạn sử dụng một trong các phương thức khởi dựng này khi chạy trên một phiên bản Windows cũ, nó sẽ ném ngoại lệ System.ArgumentException. Trên các nền Windows trước Windows Server 2003, bạn phải sử dụng mã lệnh nguyên sinh (native code) để thu lấy Windows access token mô tả người dùng cần thiết. Kế đó, bạn có thể sử dụng access token này để tạo đối tượng WindowsIdentity; mục 13.15 sẽ trình bày cách thu lấy Windows access token cho những người dùng cụ thể. Ứng dụng WindowsGroupExample dưới đây trình bày cách kiểm tra người dùng hiện hành có là thành viên của một tập các nhóm Windows được nêu tên hay không. Các nhóm này được chỉ định trong các đối số dòng lệnh; bạn nhớ đặt tên máy tính, tên miền, hay BUILTIN (đối với các nhóm Windows chuẩn) vào trước tên nhóm. using System; using System.Security.Principal; public class WindowsGroupExample { public static void Main (string[] args) { // Thu lấy đối tượng WindowsIdentity // mô tả người dùng hiện hành. WindowsIdentity identity = WindowsIdentity.GetCurrent(); // Tạo đối tượng WindowsPrincipal mô tả các khả năng bảo mật // của đối tượng WindowsIdentity được chỉ định. WindowsPrincipal principal = new WindowsPrincipal(identity); // Duyệt qua các đối số dòng lệnh (tên nhóm) và kiểm tra xem // người dùng hiện hành có là thành viên của mỗi nhóm hay không. foreach (string role in args) { Console.WriteLine("Is {0} a member of {1}? = {2}", identity.Name, role, principal.IsInRole(role)); } } } Nếu bạn chạy ví dụ này với tư cách người dùng có tên là nnbphuong81 trên một máy tính có tên là MACHINE bằng lệnh WindowsGroupExample BUILTIN\Administrators BUILTIN\Users MACHINE\Accountants, kết xuất có thể như sau: Is MACHINE\nnbphuong81 a member of BUILTIN\Administrators? = False Is MACHINE\nnbphuong81 a member of BUILTIN\Users? = True Is MACHINE\nnbphuong81 a member of MACHINE\Accountants? = True 13.14 Hạn chế những người dùng nào đó thực thi mã lệnh của bạn  Bạn cần hạn chế những người dùng nào đó truy xuất các phần tử trong mã lệnh của bạn dựa trên tên người dùng hay các vai trò mà người dùng này là thành viên. 427 Chương 13: Bảo mật  Sử dụng lớp System.Security.Permissions.PrincipalPermission và bản sao đặc tính System.Security.Permissions.PrincipalPermissionAttribute của lớp này để bảo vệ các phần tử trong chương trình của bạn với các yêu cầu RBS. .NET Framework hỗ trợ cả yêu cầu RBS bắt buộc (imperative RBS demand) và yêu cầu RBS khai báo (declarative RBS demand). Lớp PrincipalPermission hỗ trợ các lệnh bảo mật bắt buộc, và bản sao đặc tính PrincipalPermissionAttribute của lớp này hỗ trợ các lệnh bảo mật khai báo. Các yêu cầu RBS sử dụng cú pháp giống như các yêu cầu CAS, nhưng các yêu cầu RBS chỉ rõ tên mà người dùng hiện hành phải có, hoặc thông thường hơn là các vai trò mà người dùng này là thành viên. Một yêu cầu RBS lệnh cho bộ thực thi xét tên và các vai trò của người dùng hiện hành, và nếu chúng không đạt yêu cầu, bộ thực thi sẽ ném ngoại lệ System.Security.SecurityException. Đoạn mã dưới đây trình bày cú pháp của một yêu cầu bảo mật bắt buộc: // Cú pháp của một yêu cầu bảo mật bắt buộc dựa-trên-vai-trò. public static void SomeMethod() { § PrincipalPermission perm = new PrincipalPermission("UserName", "RoleName"); perm.Demand(); § } Trước tiên, bạn phải tạo một đối tượng PrincipalPermission chỉ định tên người dùng và tên vai trò mà bạn yêu cầu, rồi gọi phương thức Demand của nó. Bạn chỉ có thể chỉ định một tên người dùng và tên vai trò cho mỗi yêu cầu. Nếu tên người dùng hoặc tên vai trò là null, bất kỳ giá trị nào cũng sẽ thỏa mãn yêu cầu. Khác với các quyền truy xuất mã lệnh, một yêu cầu RBS không cho kết quả trong một stack walk; bộ thực thi chỉ đánh giá tên người dùng và các vai trò của người dùng hiện hành. Đoạn mã dưới đây trình bày cú pháp của một yêu cầu bảo mật khai báo: // Cú pháp của một yêu cầu bảo mật khai báo dựa-trên-vai-trò. [PrincipalPermission(SecurityAction.Demand, Name = "UserName", Role = "RoleName")] public static void SomeMethod() { /*...*/} Bạn có thể thay các yêu cầu RBS khai báo ở mức lớp hay mức thành viên. Các yêu cầu mức- lớp áp dụng cho tất cả các thành viên của lớp trừ khi có một yêu cầu mức-thành-viên chép đè yêu cầu mức-lớp. Nói chung, bạn có thể tự chọn hiện thực các yêu cầu RBS bắt buộc hay khai báo. Tuy nhiên, các yêu cầu bảo mật bắt buộc cho phép bạn tích hợp các yêu cầu RBS với logic của mã lệnh để thực hiện những yêu cầu RBS phức tạp. Ngoài ra, nếu không biết vai trò hay tên người dùng để yêu cầu lúc biên dịch, bạn phải sử dụng các yêu cầu bắt buộc. Các yêu cầu RBS khai báo có thuận lợi là chúng độc lập với logic của mã lệnh và dễ nhận biết hơn. Ngoài ra, bạn có thể xem các yêu cầu RBS khai báo bằng công cụ Permview.exe (đã được thảo luận trong mục 13.6). Cho dù hiện thực yêu cầu RBS bắt buộc hay khai báo, bạn cũng phải chắc rằng bộ thực thi có thể truy xuất tên và các vai trò của người dùng hiện hành để đánh giá yêu cầu một cách phù hợp. 428 Chương 13: Bảo mật Lớp System.Threading.Thread mô tả một tiểu trình của hệ điều hành (chạy mã lệnh được- quản-lý). Thuộc tính tĩnh CurrentPrincipal của lớp Thread chứa một thể hiện IPrincipal— mô tả người dùng mà tiểu trình hiện đang chạy trên danh nghĩa của người này. Ở mức hệ điều hành, mỗi tiểu trình cũng có một Windows access token kết giao—mô tả tài khoản Windows mà tiểu trình hiện đang chạy trên danh nghĩa của tài khoản này. Bạn cần hiểu rằng thể hiện IPrincipal và Windows access token là hai thực thể riêng biệt. Windows sử dụng access token để thực hiện cơ chế bảo mật hệ điều hành, trong khi bộ thực thi .NET sử dụng thể hiện IPrincipal để đánh giá các yêu cầu RBS ở mức ứng dụng. Mặc dù chúng có thể mô tả cùng một người dùng, nhưng điều này không có nghĩa là luôn luôn như vậy. Theo mặc định, thuộc tính Thread.CurrentPrincipal là không xác định. Vì việc thu lấy các thông tin liên quan đến người dùng có thể mất nhiều thời gian, và chỉ một phần nhỏ trong số các ứng dụng sử dụng thông tin này, các nhà thiết kế .NET chọn cách khởi dựng "lười" đối với thuộc tính CurrentPrincipal. Trước tiên, mã lệnh thu lấy thuộc tính Thread.CurrentPrincipal, bộ thực thi gán một thể hiện IPrincipal cho thuộc tính này theo logic sau đây: 1. Nếu miền ứng dụng mà tiểu trình hiện hành đang thực thi có một principal mặc định, thì bộ thực thi sẽ gán principal này cho thuộc tính Thread.CurrentPrincipal. Theo mặc định, miền ứng dụng không có principal mặc định. Bạn có thể thiết lập principal mặc định của một miền ứng dụng bằng cách gọi phương thức SetThreadPrincipal trên một đối tượng System.AppDomain mô tả miền ứng dụng bạn muốn cấu hình. Để gọi SetPrincipalPolicy, mã lệnh của bạn phải có phần tử ControlPrincipal của SecurityPermission. Bạn chỉ có thể thiết lập principal mặc định một lần cho mỗi miền ứng dụng; lời gọi SetThreadPrincipal thứ hai dẫn đến ngoại lệ System.Security.Policy.PolicyException. 2. Nếu miền ứng dụng không có principal mặc định, chính sách principal của miền ứng dụng sẽ xác định hiện thực IPrincipal nào sẽ được tạo ra và gán nó cho Thread.CurrentPrincipal. Để cấu hình chính sách principal cho một miền ứng dụng, bạn cần thu lấy đối tượng AppDomain mô tả miền ứng dụng và gọi phương thức SetPrincipalPolicy của đối tượng này. Phương thức SetPrincipalPolicy nhận vào một thành viên thuộc kiểu liệt kê System.Security.Principal.PrincipalPolicy, giá trị này cho biết kiểu của đối tượng IPrincipal sẽ được gán cho Thread.CurrentPrincipal. Để gọi SetPrincipalPolicy, mã lệnh của bạn phải có phần tử ControlPrincipal của SecurityPermission. Bảng 13.4 liệt kê các giá trị của PrincipalPolicy; giá trị mặc định là UnauthenticatedPrincipal. 3. Nếu mã lệnh của bạn có phần tử ControlPrincipal của SecurityPermission, bạn có thể tự tạo ra đối tượng IPrincipal và trực tiếp gán nó cho thuộc tính Thread.CurrentPrincipal. Việc này sẽ ngăn bộ thực thi gán các đối tượng IPrincipal mặc định hoặc tạo ra các đối tượng mới dựa trên chính sách principal. Bảng 13.4 Các thành viên thuộc kiểu liệt kê PrincipalPolicy Tên thành viên Mô tả 429 Chương 13: Bảo mật NoPrincipal Không có đối tượng IPrincipal nào được tạo ra, Thread.CurrentPrincipal trả về một tham chiếu null. UnauthenticatedPrincipal Một đối tượng System.Security.Principal. GenericPrincipal rỗng được tạo ra và được gán cho Thread.CurrentPrincipal. WindowsPrincipal Một đối tượng WindowsPrincipal (mô tả người dùng Windows đã đăng nhập) được tạo ra và được gán cho Thread.CurrentPrincipal. Bất kể sử dụng phương pháp nào để thiết lập IPrincipal cho tiểu trình hiện hành, bạn cũng phải làm như thế trước khi sử dụng các yêu cầu bảo mật RBS, nếu không thông tin về người dùng (IPrincipal) sẽ không có hiệu lực để bộ thực thi có thể xử lý yêu cầu. Bình thường, khi chạy trên nền Windows, bạn thiết lập chính sách principal của một miền ứng dụng là PrincipalPolicy.WindowsPrincipal để thu lấy thông tin về người dùng Windows: // Thu lấy một tham chiếu đến miền ứng dụng hiện hành. AppDomain appDomain = System.AppDomain.CurrentDomain; // Cấu hình miền ứng dụng hiện hành sao cho sử dụng các // principal dựa-trên-Windows. appDomain.SetPrincipalPolicy( System.Security.Principal.PrincipalPolicy.WindowsPrincipal); File RoleBasedSecurityExample.cs (trong đĩa CD đính kèm) minh họa cách sử dụng các yêu cầu RBS bắt buộc và khai báo. Phần thứ nhất trình bày ba phương thức được bảo vệ bằng các yêu cầu RBS bắt buộc. Nếu đối tượng Thread.CurrentPrincipal không thỏa các đòi hỏi về tên người dùng và tư cách thành viên, yêu cầu sẽ ném ngoại lệ SecurityException. public static void ProtectedMethod1() { // Một yêu cầu bảo mật bắt buộc dựa-trên-vai-trò: principal // hiện hành mô tả một định danh với tên là "nnbphuong81", // các vai trò của principal là không quan trọng. System.Security.Permissions.PrincipalPermission perm = new System.Security.Permissions.PrincipalPermission (@"MACHINE\nnbphuong81", null); perm.Demand(); } public static void ProtectedMethod2() { // Một yêu cầu bảo mật bắt buộc dựa-trên-vai-trò: principal // hiện tại là một thành viên của vai trò "Managers" hay // "Developers". Khi sử dụng PrincipalPermission, bạn chỉ có thể diễn // tả mối quan hệ OR. Đó là vì phương thức PrincipalPolicy.Intersect // luôn trả về một quyền rỗng trừ khi hai input là như nhau. // Tuy nhiên, bạn có thể sử dụng lôgic của mã lệnh để hiện thực // các điều kiện phức tạp hơn. Trong trường hợp này, tên của định // danh là không quan trọng. System.Security.Permissions.PrincipalPermission perm1 = new System.Security.Permissions.PrincipalPermission (null, @"MACHINE\Managers"); 430 Chương 13: Bảo mật System.Security.Permissions.PrincipalPermission perm2 = new System.Security.Permissions.PrincipalPermission (null, @"MACHINE\Developers"); perm1.Union(perm2).Demand(); } public static void ProtectedMethod3() { // Một yêu cầu bảo mật bắt buộc dựa-trên-vai-trò: principal // hiện tại mô tả một định danh với tên là "nnbphuong81" và // là một thành viên của vai trò "Managers". System.Security.Permissions.PrincipalPermission perm = new System.Security.Permissions.PrincipalPermission (@"MACHINE\nnbphuong81", @"MACHINE\Managers"); perm.Demand(); } Phần thứ hai trình bày ba phương thức được bảo vệ bằng các yêu cầu RBS khai báo, tương đương với các yêu cầu RBS bắt buộc vừa được trình bày ở trên: // Một yêu cầu bảo mật khai báo dựa-trên-vai-trò: principal hiện tại // mô tả một định danh với tên là "nnbphuong81", các vai trò của // principal là không quan trọng. [PrincipalPermission(SecurityAction.Demand, Name = @"MACHINE\nnbphuong81")] public static void ProtectedMethod1() { /*...*/} // Một yêu cầu bảo mật khai báo dựa-trên-vai-trò: principal hiện tại // là một thành viên của vai trò "Managers" hay "Developers". Bạn chỉ // có thể diễn tả mối quan hệ OR (không thể diễn tả mối quan hệ AND). // Tên của định danh là không quan trọng. [PrincipalPermission(SecurityAction.Demand, Role = @"MACHINE\Managers")] [PrincipalPermission(SecurityAction.Demand, Role = @"MACHINE\Developers")] public static void ProtectedMethod2() { /*...*/} // Một yêu cầu bảo mật khai báo dựa-trên-vai-trò: principal hiện tại // mô tả một định danh với tên là "nnbphuong81" và là một thành viên // của vai trò "Managers". [PrincipalPermission(SecurityAction.Demand, Name = @"MACHINE\nnbphuong81", Role = @"MACHINE\Managers")] public static void ProtectedMethod3() { /*...*/} 13.15 Giả nhận người dùng Windows  Bạn muốn mã lệnh của bạn chạy trong ngữ cảnh của một người dùng Windows nào đó chứ không phải tài khoản người dùng hiện đang tích cực.  Thu lấy đối tượng System.Security.Principal.WindowsIdentity mô tả người dùng Windows mà bạn cần giả nhận, rồi gọi phương thức Impersonate của đối tượng WindowsIdentity. 431 Chương 13: Bảo mật Mỗi tiểu trình Windows đều có một access token kết giao—mô tả tài khoản Windows mà tiểu trình hiện đang chạy trên danh nghĩa của tài khoản này. Hệ điều hành Windows sử dụng access token để xác định một tiểu trình có các quyền thích đáng để thực hiện các thao tác được-bảo-vệ trên danh nghĩa của tài khoản này hay không, như đọc/ghi file, khởi động lại hệ thống, và thay đổi thời gian hệ thống. Theo mặc định, một ứng dụng được-quản-lý chạy trong ngữ cảnh của tài khoản Windows đã thực thi ứng dụng. Điều này là hoàn toàn bình thường, nhưng đôi lúc bạn muốn chạy ứng dụng trong ngữ cảnh của một tài khoản Windows khác. Điều này đúng trong trường hợp các ứng dụng phía server cần xử lý phiên giao dịch dựa trên danh nghĩa của các người dùng kết nối đến server. Thông thường, một ứng dụng server chạy trong ngữ cảnh của tài khoản Windows được tạo riêng cho ứng dụng—đây là tài khoản dịch vụ (service account). Tài khoản dịch vụ này sẽ có các quyền tối thiểu để truy xuất các tài nguyên hệ thống, làm cho ứng dụng hoạt động như thể đó là người dùng đã kết nối cho phép ứng dụng truy xuất các hoạt động và tài nguyên phù hợp với quyền hạn của người dùng đó. Khi một ứng dụng nắm lấy định danh của một người dùng khác, đây là sự giả nhận (impersonation). Nếu được hiện thực đúng, sự giả nhận sẽ đơn giản hóa việc quản trị bảo mật và thiết kế ứng dụng, trong khi vẫn duy trì việc giải trình người dùng.  Như đã thảo luận trong mục 13.14, Windows access token và .NET principal của một tiểu trình là các thực thể riêng biệt và có thể mô tả những người dùng khác nhau. Kỹ thuật giả nhận được mô tả trong mục này chỉ thay đổi Windows access token của tiểu trình hiện hành, chứ không thay đổi principal của tiểu trình này. Để thay đổi principal của tiểu trình, mã lệnh của bạn phải có phần tử ControlPrincipal của SecurityPermission và gán một đối tượng System.Security.Principal.IPrincipal mới vào thuộc tính CurrentPrincipal của System.Threading.Thread hiện hành. Lớp System.Security.Principal.WindowsIdentity cung cấp các chức năng mà thông qua đó, bạn có thể thực hiện sự giả nhận. Tuy nhiên, quá trình này tùy thuộc vào ứng dụng của bạn đang chạy trên phiên bản Windows nào. Trên Windows Server 2003 trở về sau, lớp WindowsIdentity hỗ trợ các phiên bản nạp của chồng phương thức khởi dựng, cho phép tạo ra các đối tượng WindowsIdentity dựa trên tên tài khoản của người dùng cần giả nhận. Trên tất cả các phiên bản Windows trước đó, trước hết bạn phải thu lấy System.IntPtr chứa tham chiếu đến Windows access token mô tả người dùng cần giả nhận. Để thu lấy tham chiếu này, bạn cần sử dụng một phương thức nguyên sinh như LogonUser của Win32 API.  Vấn đề chủ yếu khi thực hiện sự giả nhận trên Windows 2000 và Windows NT là một tài khoản phải có đặc quyền SE_TCB_NAME thì mới có thể thực thi LogonUser. Điều này đòi hỏi bạn cấu hình chính sách bảo mật của Windows và cấp cho tài khoản quyền “Act as part of operating system” (mức tin cậy rất cao). Bạn đừng bao giờ trực tiếp cấp đặc quyền SE_TCB_NAME cho các tài khoản người dùng. Một khi đã có đối tượng WindowsIdentity mô tả người dùng cần giả nhận, bạn hãy gọi phương thức Impersonate của nó. Từ lúc này, tất cả các hành động mà mã lệnh của bạn thực hiện đều diễn ra trong ngữ cảnh của tài khoản Windows đã được giả nhận. Phương thức Impersonate trả về đối tượng System.Security.Principal.WindowsSecurityContext, đối 432 Chương 13: Bảo mật tượng này mô tả tài khoản tích cực trước khi giả nhận. Để trở về tài khoản cũ, bạn cần gọi phương thức Undo của đối tượng WindowsSecurityContext này. Ứng dụng dưới đây trình bày sự giả nhận của một người dùng Windows. Ứng dụng này cần hai đối số dòng lệnh: tên tài khoản của người dùng cần giả nhận và password của tài khoản. Ứng dụng này sử dụng hàm LogonUser của Win32 API để thu lấy Windows access token cho người dùng được chỉ định, giả nhận người dùng này, rồi trở về ngữ cảnh của người dùng cũ. Ví dụ, lệnh ImpersonationExample nnbphuong81 password sẽ giả nhận người dùng nnbphuong81 nếu người dùng đó đã tồn tại trong cơ sở dữ liệu tài khoản cục bộ. using System; using System.IO; using System.Security.Principal; using System.Security.Permissions; using System.Runtime.InteropServices; // Bảo đảm assembly có quyền truy xuất mã lệnh không-được-quản-lý // và có quyền kiểm soát principal của tiểu trình. [assembly:SecurityPermission(SecurityAction.RequestMinimum, UnmanagedCode=true, ControlPrincipal=true)] public class ImpersonationExample { // Định nghĩa các hằng số được sử dụng cùng với hàm LogonUser. const int LOGON32_PROVIDER_DEFAULT = 0; const int LOGON32_LOGON_INTERACTIVE = 2; // Nhập hàm Win32 LogonUser từ advapi32.dll. Chỉ định // "SetLastError = true" để có thể truy xuất các mã lỗi của Win32. [DllImport("advapi32.dll", SetLastError=true)] static extern int LogonUser(string userName, string domain, string password, int logonType, int logonProvider, ref IntPtr accessToken); public static void Main(string[] args) { // Tạo một IntPtr mới để giữ lấy access token // do hàm LogonUser trả về. IntPtr accessToken = IntPtr.Zero; // Gọi LogonUser để thu lấy access token cho người dùng // được chỉ định. Biến accessToken được truyền cho LogonUser // bằng tham chiếu và sẽ chứa tham chiếu đến Windows access token // nếu LogonUser thành công. int result = LogonUser( args[0], // tên người dùng để đăng nhập ".", // sử dụng CSDL tài khoản cục bộ args[1], // password của người dùng LOGON32_LOGON_INTERACTIVE, // tạo một interactive login LOGON32_PROVIDER_DEFAULT, // sử dụng logon provider mặc định ref accessToken // nhận access token handle ); // Nếu lỗi xảy ra (LogonUser trả về zero), hiển thị lỗi và thoát. if (result == 0) { Console.WriteLine("LogonUser returned error {0}", Marshal.GetLastWin32Error()); } else { 433 Chương 13: Bảo mật // Tạo một WindowsIdentity mới từ Windows access token. WindowsIdentity identity = new WindowsIdentity(accessToken); // Hiển thị định danh đang tích cực (trước khi giả nhận). Console.WriteLine("Identity before impersonation = {0}", WindowsIdentity.GetCurrent().Name); // Giả nhận người dùng đã được chỉ định. Đối tượng // WindowsImpersonationContext chứa các thông tin // cần thiết để trở về ngữ cảnh của người dùng cũ. WindowsImpersonationContext impContext = identity.Impersonate(); // Hiển thị định danh đang tích cực (trong lúc giả nhận). Console.WriteLine("Identity during impersonation = {0}", WindowsIdentity.GetCurrent().Name); // *************************************************************** // Thực hiện các hành động với danh nghĩa người dùng được giả nhận // *************************************************************** // Trở về người dùng Windows cũ bằng đối tượng // WindowsImpersonationCo

Các file đính kèm theo tài liệu này:

  • pdfcac_giai_phap_lap_trinh_c_sharp_split_9.pdf
Tài liệu liên quan