2011-02-03 1 views
7

즉, 이것은 정말 어리석은 생각입니까?영역, 컨트롤러 및 동작에 대한 사용자 지정 AuthorizeAttribute를 어떻게 만듭니 까?

[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); 
    } 
} 

UPDATE 나는 역할은 클라이언트 당 기준으로 설정하고 사용자 그룹에 부착하기 때문에 우리는 매우 세분화 된 역할 권한이 시나리오에서, 다음을 피하려고 노력 해요 :

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(); 
    } 
} 

누군가이 라우드 정보에 액세스하고이를 역할 이름으로 사용하기 위해이 AuthorizeRouteAttribute를 작성하는 안전한 방법을 알 수 있습니까? Levi가 말한 것처럼 RouteData.Values는 안전하지 않습니다.

실행중인 httpContext.Request.Path의 사용이 더 안전하거나 더 나은 방법입니까?

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; 
} 

답변

18

이 작업을 수행하지 마십시오 :

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); 
} 

이 어쩌면 조금 더 문제를 보여줍니다.

당신이 정말로, 보안 결정을 내릴 컨트롤러의 유형 또는 행동의 MethodInfo를 사용해야하는 경우

. 그러나 모든 것을 끈으로 묶어 놓는 것은 문제를 요구하고 있습니다. 실제 컨트롤러에 대한 라우팅 값의 1 : 1 매핑은 보장되지 않습니다. SomeController :: SomeAction에 대한 액세스를 확인하기 위해 라우팅 튜플 (a, b, c)을 사용하고 있지만 누군가가 (a, b ', c)가 동일한 액션을 수행한다는 사실을 발견하면 보안 메커니즘을 무시할 수 있습니다.

편집는 의견에 응답하기 :

당신은 filterContext 매개 변수의 ActionDescriptor 속성을 통해 컨트롤러의 유형과 작업의 MethodInfo에 액세스 할 수 있습니다. 이는 MVC 파이프 라인이 처리 될 때 이 실제로 어떤 동작을 실행할지를 결정하는 유일한 확실한 방법입니다. MVC 파이프 라인이 처리 중일 때 실행됩니다. 왜냐하면 조회가 MVC의 장면 뒤에서 일어나는 일과 정확히 일치하지 않기 때문일 수 있습니다. Type/MethodInfo /가 있으면 원하는 정보 (예 : 정규화 된 이름)를 사용하여 보안 결정을 내릴 수 있습니다.

컨트롤러 FooController 및 동작 TheAction이있는 MyArea 영역을 생각해보십시오.

/MyArea/푸/TheAction

그리고 라우팅 (지역 = "MyArea을"튜플을 제공, 컨트롤러 = ": 일반적으로이 FooController를 공격 할 방법은 :: TheAction이 URL을 통해입니다 Foo ", Action ="TheAction ").

/푸/TheAction

그리고 라우팅 튜플을 줄 것이다 (지역 = "", 컨트롤러 = "푸 :

그러나, 당신은이 URL을 통해 FooController :: TheAction을 칠 수있다 ", Action ="TheAction ").영역은 라우트와 연관되며 컨트롤러는 연관되지 않는다는 것을 기억하십시오. 그리고 컨트롤러가 여러 경로에 의해 공격받을 수 있기 때문에 (정의가 일치하는 경우) 컨트롤러는 여러 영역과 논리적으로 연관 될 수 있습니다. 따라서 개발자가 보안 결정을 내리기 위해 경로 (또는 영역 또는 위치 > 태그)를 절대 사용하지 말라고 개발자에게 말합니다.

또한 클래스에 버그가있어서 변경 가능합니다 (OnAuthorization에서 자체 Roles 속성을 변경 함). 액션 필터 속성은 파이프 라인의 일부분에 의해 캐시되고 재사용 될 수 있기 때문에 불변이어야한다. 응용 프로그램에서이 특성이 선언 된 위치에 따라 타이밍 공격이 열리 며 악의적 인 사이트 방문자는 자신이 원하는 모든 동작에 액세스 할 수 있도록 악용 할 수 있습니다.

+0

제안 사항이 코드에서 어떻게 작동하는지 보여주는 답변을 추가 할 수 있습니까? 우리는 지역을 사용하고 있으므로 컨트롤러와 액션뿐만 아니라이를 반영해야합니다. 관심이 없으면 컨트롤러 또는 작업 (예 :/myarea/mycontroller/myaction '; DROP TABLE members; - /)에 실제로 일치시킬 수 있습니까? 확실히 MVC는 처음부터 컨트롤러 나 액션과 일치하지 않을 것입니다. – Junto

+0

질문에 대한 답변을 업데이트했습니다. – Levi

+0

안녕하세요 Levi, 사용할 수 있습니다 (문자열 컨트롤러 = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; 문자열 동작 = filterContext.ActionDescriptor.ActionName;)하지만 같은 방법으로 영역 이름에 액세스 할 수 없습니다. 그러나 사용 가능한 AreaName이 없습니다. 어디에서 찾을 수 있습니까? 간단한 코드 예제가이 질문을 닫습니다. – Junto

5

당신이 계정에 리바이스 추천을 복용, 이렇게 할 경우, 대답은 다음과 같다 :

더 많은 정보를 원하시면, 또한 나의 응답을 참조하십시오

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); 
     } 
    } 
} 

나는 HttpU를 제공하고 싶지 않았다. 결과적으로 사용자를 로그인 페이지로 보내기 때문에 사용자가 역할을 수행하지 않는 경우 nauthorizedResult입니다. 사용자가 이미 로그인 한 경우 이는 매우 혼란 스럽습니다.

+1

대신에 OnAuthorization을 사용하여 AuthorizeCore를 사용하는 대신에 - http://stackoverflow.com/questions/5989100/asp-net-mvc-3- custom-authorisation? – gw0

+0

@ gw0, 어떻게'AuthorizeCore'에'filterContext'를 얻으시겠습니까? –

+0

@Junto,'base를 호출하는 문제는 무엇입니까?OnAuthorization (filterContext);'사용자 정의 OnAuthorization()의 끝에서? –

1

예고편입니다. filterContext.RouteData.Values["area"];

Good Luck 대신 filterContext.RouteData.DataTokens["area"];를 사용해야합니다.

관련 문제