Bảo mật phần cuối

Chia sẻ: Nghia Bui Tuan | Ngày: | Loại File: PDF | Số trang:9

0
48
lượt xem
6
download

Bảo mật phần cuối

Mô tả tài liệu
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

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.

Chủ đề:
Lưu

Nội dung Text: Bảo mật phần cuối

  1. 1.1 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. 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() { /*...*/}
  2. 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. 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.
  3. 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ả Không có đối tượng IPrincipal nào NoPrincipal được tạo ra, Thread.CurrentPrincipal trả về một tham chiếu null. Một đối tượng System.Security.Principal. UnauthenticatedPrincipal GenericPrincipal rỗng được tạo ra và được gán cho Thread.CurrentPrincipal. Một đối tượng WindowsPrincipal (mô tả người dùng Windows đã đăng nhập) WindowsPrincipal đượ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;
  4. // 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"); System.Security.Permissions.PrincipalPermission perm2 = new System.Security.Permissions.PrincipalPermission (null, @"MACHINE\Developers");
  5. 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",
  6. Role = @"MACHINE\Managers")] public static void ProtectedMethod3() { /*...*/} 1.2 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. 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
  7. 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 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.
  8. 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 { // Tạo một WindowsIdentity mới từ Windows access token. WindowsIdentity identity = new WindowsIdentity(accessToken);
  9. // 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 // WindowsImpersonationContext. impContext.Undo();as // Hiển thị định danh đang tích cực (sau khi giả nhận). Console.WriteLine("Identity after impersonation = {0}", WindowsIdentity.GetCurrent().Name); } } }
Đồng bộ tài khoản