2011-02-03 26 views
7

Nói cách khác, đây có phải là một ý tưởng thực sự ngu ngốc?Làm cách nào để tạo một AuthorizeAttribute tùy chỉnh dành riêng cho khu vực, bộ điều khiển và hành động?

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
public class AuthorizeActionAttribute : AuthorizeAttribute 
{ 
    public override void OnAuthorization(AuthorizationContext filterContext) 
    { 
     // get the area, controller and action 
     var area = filterContext.RouteData.Values["area"]; 
     var controller = filterContext.RouteData.Values["controller"]; 
     var action = filterContext.RouteData.Values["action"]; 
     string verb = filterContext.HttpContext.Request.HttpMethod; 

     // these values combined are our roleName 
     string roleName = String.Format("{0}/{1}/{2}/{3}", area, controller, action, verb); 

     // set role name to area/controller/action name 
     this.Roles = roleName; 

     base.OnAuthorization(filterContext); 
    } 
} 

CẬP NHẬT Tôi đang cố gắng để tránh những điều sau đây, trong một kịch bản mà chúng tôi có quyền vai trò cực kỳ hạt vì những vai trò được thiết lập trên một cơ sở cho mỗi khách hàng và gắn liền với nhóm người dùng:

public partial class HomeController : Controller 
{ 
    [Authorize(Roles = "/supplierarea/homecontroller/indexaction/")] 
    public virtual ActionResult Index() 
    { 
     return View(); 
    } 

    [Authorize(Roles = "/supplierarea/homecontroller/aboutaction/")] 
    public virtual ActionResult About() 
    { 
     return View(); 
    } 
} 

Bất cứ ai có thể khai sáng cho tôi một cách an toàn để viết AuthorizeRouteAttribute này để truy cập thông tin tuyến đường và sử dụng tên này làm tên vai trò? Như Levi nói, RouteData.Values ​​không an toàn.

Việc sử dụng tệp httpContext.Request.Path có thực thi an toàn hơn hay tốt hơn không?

public override void OnAuthorization(AuthorizationContext filterContext) 
{ 
    if (filterContext == null) 
    { 
     throw new ArgumentNullException("filterContext"); 
    } 

    if (!filterContext.HttpContext.User.Identity.IsAuthenticated) 
    { 
     // auth failed, redirect to login page 
     filterContext.Result = new HttpUnauthorizedResult(); 
     return; 
    } 

    var path = filterContext.HttpContext.Request.Path; 
    var verb = filterContext.HttpContext.Request.HttpMethod; 

    // these values combined are our roleName 
    string roleName = String.Format("{0}/{1}", path, verb); 

    if (!filterContext.HttpContext.User.IsInRole(roleName)) 
    { 
     // role auth failed, redirect to login page 
     filterContext.Result = new HttpUnauthorizedResult(); 
     // P.S. I want to tell the logged in user they don't 
     // have access, not ask them to login. They are already 
     // logged in! 
     return; 
    } 

    // 
    base.OnAuthorization(filterContext); 
} 

này có thể minh họa cho vấn đề xa hơn một chút:

enum Version 
{ 
    PathBasedRole, 
    InsecureButWorks, 
    SecureButMissingAreaName 
} 

string GetRoleName(AuthorizationContext filterContext, Version version) 
{ 
    // 
    var path = filterContext.HttpContext.Request.Path; 
    var verb = filterContext.HttpContext.Request.HttpMethod; 

    // recommended way to access controller and action names 
    var controller = 
     filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; 
    var action = 
     filterContext.ActionDescriptor.ActionName; 
    var area = "oh dear...."; // mmmm, where's thearea name??? 

    // 
    var insecureArea = filterContext.RouteData.Values["area"]; 
    var insecureController = filterContext.RouteData.Values["controller"]; 
    var insecureAction = filterContext.RouteData.Values["action"]; 

    string pathRoleName = 
     String.Format("{0}/{1}", path, verb); 
    string insecureRoleName = 
     String.Format("{0}/{1}/{2}/{3}", 
     insecureArea, 
     insecureController, 
     insecureAction, 
     verb); 
    string secureRoleName = 
     String.Format("{0}/{1}/{2}/{3}", 
     area, 
     controller, 
     action, 
     verb); 

    string roleName = String.Empty; 

    switch (version) 
    { 
     case Version.InsecureButWorks: 
      roleName = insecureRoleName; 
      break; 
     case Version.PathBasedRole: 
      roleName = pathRoleName; 
      break; 
     case Version.SecureButMissingAreaName: 
      // let's hope they don't choose this, because 
      // I have no idea what the area name is 
      roleName = secureRoleName; 
      break; 
     default: 
      roleName = String.Empty; 
      break; 
    } 

    return roleName; 
} 

Trả lời

18

Hãy không làm điều này.

Nếu bạn thực sự cần, bạn có thể sử dụng Loại của bộ điều khiển hoặc MethodInfo của hành động để đưa ra quyết định bảo mật. Nhưng dựa trên tất cả mọi thứ tắt của dây là yêu cầu cho sự cố. Hãy nhớ rằng, không có bản đồ 1: 1 được bảo đảm về các giá trị Định tuyến tới bộ điều khiển thực tế. Nếu bạn đang sử dụng bộ định tuyến (a, b, c) để xác nhận quyền truy cập vào SomeController :: SomeAction nhưng ai đó phát hiện ra rằng (a, b ', c) cũng truy cập cùng một hành động, người đó có thể bỏ qua các cơ chế bảo mật của bạn.

Sửa trả lời nhận xét:

Bạn có thể sử dụng loại của bộ điều khiển và MethodInfo của hành động thông qua tài sản ActionDescriptor các filterContext tham số của. Đây là cách duy nhất để xác định hành động nào sẽ thực hiện thực sự khi đường dẫn MVC đang xử lý vì có thể tra cứu của bạn không khớp chính xác với những gì đang diễn ra đằng sau hậu trường với MVC. Một khi bạn có Type/MethodInfo/bất cứ điều gì, bạn có thể sử dụng bất kỳ thông tin nào bạn muốn (chẳng hạn như tên đầy đủ của họ) để đưa ra quyết định bảo mật.

Ví dụ thực tế, hãy xem xét vùng MyArea bằng bộ điều khiển FooController và hành động TheAction. Thông thường cách mà bạn sẽ đạt FooController này :: TheAction là thông qua URL này:

/MyArea/Foo/TheAction

Và Routing cung cấp cho các tuple (Diện tích = "MyArea", điều khiển = " Foo ", Hành động =" Hành động ").

Tuy nhiên, bạn cũng có thể nhấn FooController :: TheAction qua URL này:

/Foo/TheAction

Và Routing sẽ cung cấp cho các tuple (Diện tích = "", điều khiển = "Foo ", Hành động =" Hành động ").Hãy nhớ rằng, các khu vực được liên kết với các tuyến đường, chứ không phải các bộ điều khiển. Và kể từ khi một bộ điều khiển có thể được nhấn bởi nhiều tuyến đường (nếu các định nghĩa phù hợp), sau đó một bộ điều khiển cũng có thể được liên kết hợp lý với nhiều khu vực. Đây là lý do tại sao chúng tôi yêu cầu nhà phát triển không bao giờ sử dụng tuyến đường (hoặc khu vực hoặc < vị trí > thẻ, theo tiện ích mở rộng) để đưa ra quyết định bảo mật.

Ngoài ra, có một lỗi trong lớp học của bạn ở chỗ nó có thể thay đổi (nó biến đổi thuộc tính Vai trò của riêng nó trong OnAuthorization). Thuộc tính của bộ lọc hành động phải không thay đổi, vì chúng có thể được lưu trong bộ nhớ cache bởi các phần của đường ống và được sử dụng lại. Tùy thuộc vào nơi thuộc tính này được khai báo trong ứng dụng của bạn, điều này sẽ mở ra một cuộc tấn công thời gian, mà một người truy cập trang web độc hại có thể khai thác để cấp quyền truy cập cho bất kỳ hành động nào mà anh ta mong muốn.

Mọi chi tiết, xem thêm phản ứng của tôi tại địa chỉ:

+0

Bạn có thể thêm câu trả lời của mình để hiển thị cách đề xuất hoạt động trong mã không? Chúng tôi đang sử dụng các khu vực, vì vậy nó sẽ cần phải phản ánh điều này cũng như các bộ điều khiển và hành động. Không quan tâm, bạn có thể thực sự phù hợp với bộ điều khiển hoặc hành động (ví dụ như được đề xuất:/myarea/mycontroller/myaction '; DROP TABLE thành viên; - /)? Chắc chắn MVC sẽ không phù hợp với bộ điều khiển hay hành động ngay từ đầu? – Junto

+0

Đã cập nhật câu trả lời để giải quyết câu hỏi của bạn. – Levi

+0

Hi Levi, tôi hiểu rằng tôi có thể sử dụng (string controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; string action = filterContext.ActionDescriptor.ActionName;) nhưng tôi không thể truy cập vào tên khu vực theo cùng một cách. Tuy nhiên, không có AreaName có sẵn. Tôi có thể xác định vị trí đó ở đâu? Một ví dụ mã đơn giản sẽ đóng câu hỏi này. – Junto

5

Nếu bạn muốn làm điều này, lấy khuyến nghị Levi vào tài khoản, câu trả lời là như sau:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.Mvc; 
using System.Web.Security; 

namespace MvcApplication1.Extension.Attribute 
{ 
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
    public class AuthorizeActionAttribute : AuthorizeAttribute 
    { 
     /// <summary> 
     /// Called when a process requests authorization. 
     /// </summary> 
     /// <param name="filterContext">The filter context, which encapsulates information for using <see cref="T:System.Web.Mvc.AuthorizeAttribute"/>.</param> 
     /// <exception cref="T:System.ArgumentNullException">The <paramref name="filterContext"/> parameter is null.</exception> 
     public override void OnAuthorization(AuthorizationContext filterContext) 
     { 
      if (filterContext == null) 
      { 
       throw new ArgumentNullException("filterContext"); 
      } 

      if (!filterContext.HttpContext.User.Identity.IsAuthenticated) 
      { 
       // auth failed, redirect to login page 
       filterContext.Result = new HttpUnauthorizedResult(); 

       return; 
      } 

      // these values combined are our roleName 
      string roleName = GetRoleName(filterContext); 

      if (!filterContext.HttpContext.User.IsInRole(roleName)) 
      { 
       filterContext.Controller.TempData.Add("RedirectReason", "You are not authorized to access this page."); 
       filterContext.Result = new RedirectResult("~/Error/Unauthorized"); 

       return; 
      } 

      // 
      base.OnAuthorization(filterContext); 
     } 

     /// <summary> 
     /// Gets the name of the role. Theorectical construct that illustrates a problem with the 
     /// area name. RouteData is apparently insecure, but the area name is available there. 
     /// </summary> 
     /// <param name="filterContext">The filter context.</param> 
     /// <param name="version">The version.</param> 
     /// <returns></returns> 
     string GetRoleName(AuthorizationContext filterContext) 
     { 
      // 
      var verb = filterContext.HttpContext.Request.HttpMethod; 

      // recommended way to access controller and action names 
      var controllerFullName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerType.FullName; 
      var actionName = filterContext.ActionDescriptor.ActionName; 

      return String.Format("{0}.{1}-{2}", controllerFullName, actionName, verb); 
     } 
    } 
} 

Tôi không muốn cung cấp một HttpU nauthorizedResult trong trường hợp người dùng không có vai trò, vì kết quả là gửi người dùng đến trang đăng nhập. Xem xét rằng họ đã đăng nhập, điều này là cực kỳ khó hiểu cho người dùng.

+1

Điều gì thay vì sử dụng OnAuthorization để sử dụng AuthorizeCore - http://stackoverflow.com/questions/5989100/asp-net-mvc-3-custom-authorisation? – gw0

+0

@ gw0, Bạn giả sử thế nào để có được 'filterContext' trong' AuthorizeCore'? –

+0

@ Junto, vấn đề gọi là 'cơ sở là gì.OnAuthorization (filterContext); 'vào cuối tùy chọn 'OnAuthorization()' của bạn? –

1

Đây là thông báo ngắn! Hãy chắc chắn sử dụng filterContext.RouteData.DataTokens["area"]; thay vì filterContext.RouteData.Values["area"];

Chúc may mắn.

Các vấn đề liên quan