如何创建一个自定义AuthorizeAttribute特定于该地区,控制器和行动? [英] How do I create a custom AuthorizeAttribute that is specific to the area, controller and action?

查看:186
本文介绍了如何创建一个自定义AuthorizeAttribute特定于该地区,控制器和行动?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在换句话说,这是一个非常愚蠢的想法?

  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)
公共类AuthorizeActionAttribute:AuthorizeAttribute
{
    公共覆盖无效OnAuthorization(AuthorizationContext filterContext)
    {
        //获取方面,控制器和行动
        VAR面积= filterContext.RouteData.Values​​ [区];
        VAR控制器= filterContext.RouteData.Values​​ [控制器];
        VAR行动= filterContext.RouteData.Values​​ [行动];
        串动词= filterContext.HttpContext.Request.HttpMethod;        //这些值结合起来,是我们的角色名
        字符串角色名=的String.Format({0} / {1} / {2} / {3},面积,控制器,动作动词);        //设置角色名区/控制器/动作名称
        this.Roles =角色名;        base.OnAuthorization(filterContext);
    }
}

更新
我试图避免以下,在我们拥有极其精细的角色权限,因为角色是在每个客户端的基础设置和连接到用户群的情景:

 公共部分类HomeController的:控制器
{
    [授权(角色=/ supplierarea / HomeController中/的indexAction /)]
    公共虚拟的ActionResult指数()
    {
        返回查看();
    }    [授权(角色=/ supplierarea / HomeController中/ aboutaction /)]
    公共虚拟的ActionResult关于()
    {
        返回查看();
    }
}

任何人都可以启发我写这篇AuthorizeRouteAttribute访问路由信息,并以此作为角色名以安全的方式?正如列维称,RouteData.Values​​是不安全的。

是利用执行httpContext.Request.Path任何更安全或更好的做法?

 公共覆盖无效OnAuthorization(AuthorizationContext filterContext)
{
    如果(filterContext == NULL)
    {
        抛出新的ArgumentNullException(filterContext);
    }    如果(!filterContext.HttpContext.User.Identity.IsAuthenticated)
    {
        //验证失败,重定向到登录页面
        filterContext.Result =新HttpUnauthorizedResult();
        返回;
    }    VAR路径= filterContext.HttpContext.Request.Path;
    VAR动词= filterContext.HttpContext.Request.HttpMethod;    //这些值结合起来,是我们的角色名
    字符串角色名=的String.Format({0} / {1},路径动词);    如果(!filterContext.HttpContext.User.IsInRole(角色名))
    {
        //角色验证失败,重定向到登录页面
        filterContext.Result =新HttpUnauthorizedResult();
        //附:我想告诉登录的用户,他们不
        //访问,而不是要求他们登录。他们已经
        // 登录!
        返回;
    }    //
    base.OnAuthorization(filterContext);
}

这或许说明了问题的进一步一点:

 枚举版本
{
    PathBasedRole,
    InsecureButWorks,
    SecureButMissingAreaName
}字符串GetRoleName(AuthorizationContext filterContext,版版)
{
    //
    VAR路径= filterContext.HttpContext.Request.Path;
    VAR动词= filterContext.HttpContext.Request.HttpMethod;    //推荐的方式来访问控制器和动作名称
    VAR控制器=
        filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
    VAR行动=
        filterContext.ActionDescriptor.ActionName;
    VAR面积=哦,亲爱的......; // MMMM,哪里还有thearea名字?    //
    VAR insecureArea = filterContext.RouteData.Values​​ [区];
    VAR insecureController = filterContext.RouteData.Values​​ [控制器];
    VAR insecureAction = filterContext.RouteData.Values​​ [行动];    串pathRoleName =
        的String.Format({0} / {1},路径动词);
    串insecureRoleName =
        的String.Format({0} / {1} / {2} / {3},
        insecureArea,
        insecureController,
        insecureAction,
        动词);
    串secureRoleName =
        的String.Format({0} / {1} / {2} / {3},
        区,
        控制器
        行动,
        动词);    字符串角色名=的String.Empty;    开关(版)
    {
        案例Version.InsecureButWorks:
            角色名= insecureRoleName;
            打破;
        案例Version.PathBasedRole:
            角色名= pathRoleName;
            打破;
        案例Version.SecureButMissingAreaName:
            //让我们希望他们不要选择这个,因为
            //我不知道该地区的名字是什么
            角色名= secureRoleName;
            打破;
        默认:
            角色名=的String.Empty;
            打破;
    }    返回角色名;
}


解决方案

不要做到这一点。

如果你真的需要,可以使用的键入的控制器或的MethodInfo 的行动作出安全决定的。但立足事事休的字符串是自找麻烦。请记住,有没有保证1:路由值1映射实际控制人。如果您使用路由元组(A,B,C),以验证访问SomeController :: SomeAction但有人发现(A,B',C)也碰到同样的行动的人,可以绕过你的安全机制。

修改以回应评论:

您已经通过的 filterContext 的参数的ActionDescriptor财产访问控制器的类型和操作的MethodInfo的。这是唯一可靠的方式来确定哪些行为会的真正的执行,当MVC管道的处理,因为它可能是您的查找不完全匹配发生了什么与MVC幕后。一旦你的类型/的MethodInfo /不管,你可以使用你想要的任何信息(如他们的完全限定名称)作出安全决定。

作为一个实际的例子,考虑与控制器FooController的和动作TheAction面积MyArea。通常情况下,你会打这个FooController的:: TheAction的方法就是通过这个网址:


  

/ MyArea /美孚/ TheAction


和路由给人的元组(面积=MyArea,控制器=富,行动=TheAction)。

不过,您也可以通过这个网址打FooController的:: TheAction:


  

/美孚/ TheAction


和路由会给元组(面积=,控制器=富,动作=TheAction)。请记住,区域与路线,不是控制器关联。并且由于控制器可通过多种途径被击中(如果定义匹配)时,则控制器还可以在逻辑上与多个区域相关联。这就是为什么我们告诉开发人员从来没有使用的路线。(或区域或在<位置>标签,通过扩展)做出安全决策

此外,还有在你的类,它是可变的(它变异了自己的角色属性OnAuthorization)的错误。行动过滤器的属性必须是不可变的,因为他们可以通过管道部分被缓存和重用。具体情况取决于该属性是您的应用程序中声明,这将打开一个定时攻击,恶意网站访问者可以利用再授予自己获得他希望的任何行动。

有关更多信息,参见我的回答:

In other words, is this a really stupid idea?

[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 I'm trying to avoid the following, in a scenario where we have extremely granular role permissions because the roles are setup on a per-client basis and attached to user groups:

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

Can anyone enlighten me to a secure way to write this AuthorizeRouteAttribute to access the route information and use this as the role name? As Levi says, the RouteData.Values isn't secure.

Is the use of the executing httpContext.Request.Path any more secure or better practice?

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

This maybe illustrates the issue a little further:

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

解决方案

Please do not do this.

If you really need to, you can use the Type of the controller or the MethodInfo of the action to make security decisions. But basing everything off of strings is asking for trouble. Remember, there's no guaranteed 1:1 mapping of Routing values to actual controller. If you're using the Routing tuple (a, b, c) to validate access to SomeController::SomeAction but somebody discovers that (a, b', c) also hits that same action, that person can bypass your security mechanisms.

Edit to respond to comments:

You have access to the controller's Type and the action's MethodInfo via the filterContext parameter's ActionDescriptor property. This is the only sure-fire way to determine what action will really execute when the MVC pipeline is processing, because it's possible that your lookup doesn't exactly match what's going on behind the scenes with MVC. Once you have the Type / MethodInfo / whatever, you can use whatever information you wish (such as their fully-qualified names) to make security decisions.

As a practical example, consider an area MyArea with a controller FooController and an action TheAction. Normally the way that you would hit this FooController::TheAction is via this URL:

/MyArea/Foo/TheAction

And Routing gives the tuple (Area = "MyArea", Controller = "Foo", Action = "TheAction").

However, you can also hit FooController::TheAction via this URL:

/Foo/TheAction

And Routing will give the tuple (Area = "", Controller = "Foo", Action = "TheAction"). Remember, areas are associated with routes, not controllers. And since a controller can be hit by multiple routes (if the definitions match), then a controller can also be logically associated with multiple areas. This is why we tell developers never to use routes (or areas or the <location> tag, by extension) to make security decisions.

Additionally, there's a bug in your class in that it's mutable (it mutates its own Roles property in OnAuthorization). Action filter attributes must be immutable, since they may be cached by parts of the pipeline and reused. Depending on where this attribute is declared in your application, this opens a timing attack, which a malicious site visitor could then exploit to grant himself access to any action he wishes.

For more info, see also my responses at:

这篇关于如何创建一个自定义AuthorizeAttribute特定于该地区,控制器和行动?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆