【问题标题】:MVC security role check in the ViewModelViewModel 中的 MVC 安全角色检查
【发布时间】:2014-03-15 23:16:18
【问题描述】:

在 ViewModel 中放置与安全相关的属性以在 View 中使用,即根据用户的角色显示/隐藏“东西”是否是一种良好的安全实践?

例如:

ViewModel 属性 AdminRole。在控制器(User.IsInRole)中设置它的值,然后在访问属性的视图中: if (Model.AdminRole) { show admin stuff... }

我已经阅读了其他 SO 帖子并且(有些)人正在这样做,但质疑这是否安全,即在 ViewModel 中公开安全属性。如果有更好、更安全的方法,请告诉我。

离题,但相关:恕我直言,这比直接在视图中调用 User.IsInRole 要好得多。

【问题讨论】:

  • 您是否担心它“不安全”,因为任何可以访问ViewModel 的人都可以控制AdminRole 的值?
  • 担心 ViewModel 以某种方式从 http 请求中被劫持(如果这甚至可能的话),他们将 AdminRole 属性设置为 true,从而绕过 IsInRole 身份验证。
  • 如果您的 ViewModel 被传递到您的操作方法中,并且变量分配是通过模型绑定完成的,那么,是的,它可能会被大规模分配漏洞 (odetocode.com/blogs/scott/archive/2012/03/12/…) 劫持。如果这是一个问题,我会使用任何其他方法将数据放入您的视图中。
  • 我正在使用传递给操作方法的视图模型,因此使用默认模型绑定,并且容易受到批量分配漏洞的影响。谢谢,不知道这个。这回答了我的问题。请将此作为答案发布。

标签: asp.net-mvc asp.net-identity


【解决方案1】:

如果“ViewModel”指的是用于 MVC 视图的 DTO(相对于 MVVM 框架中的 ViewModel),那么不,这不是一个好的设计。

首先,从安全角度来看,这是一个糟糕的设计,因为:

  1. 您依赖您的视图来实际执行安全规则(例如,通过检查AdminRole 有条件地呈现内容)。这很难有效地测试或审查。

  2. 由于错误或编码草率,您可能会无意中将私人安全信息泄露给客户端。

  3. 如果没有适当的清理,您可能会在POSTPUT 或其他“写入”操作中意外使用此属性。

但更重要的是,从 MVC 的角度来看,这简直就是糟糕的设计,因为它错过了 View Model 应有的意义。

视图模型旨在包含有关如何显示视图的信息。他们应该抽象原本会进入视图的业务逻辑,而不仅仅是传递

这个场景的更好的设计是这样的:

视图模型

public class IndexViewModel
{
    public bool CanDeleteItems { get; set; }
    public bool IsAdminMenuVisible { get; set; }
    // Other properties...
}

控制器

public ActionResult Index()
{
    return View(new IndexViewModel
    {
        CanDeleteItems = User.IsInRole("ContentManager"),
        IsAdminMenuVisible = User.IsInRole("Administrator"),
        // Other properties...
    });
}

查看

@if (Model.IsAdminMenuVisible)
{
    <!-- Markup for admin menu -->
}

@foreach (var item in Model.Items)
{
    <!-- Markup for item -->
    <button type="submit"  @((Model.CanDeleteItems) ? "disabled" : "")>Delete</button>
}

这里的想法是 ViewModel 只包含特定于视图本身的属性。视图无法决定在什么业务条件下某个元素可见、禁用等。视图模型将告诉它确切显示什么以及何时显示,使用以它们绑定到的特定视图元素命名的属性。

这对可维护性也更好。如果您决定如果用户拥有“ContentManager”或“Administrator”角色,他们应该能够删除项目,会发生什么?在您的版本中,您最终会修改视图;在上述版本中,您只需要修改控制器。如果您发现自己不得不修改视图而不是更改外观和感觉,这意味着您在架构中犯了一个错误。安全检查应该发生在控制器中。


注意,根据您的架构风格,您也可以在 ViewModel 中将这些作为派生属性实现,例如:

public class IndexViewModel
{
    private readonly IPrincipal user;

    public IndexViewModel(IPrincipal user)
    {
        this.user = user;
    }

    public bool CanDeleteItems
    {
        get { return user.IsInRole("ContentManager"); }
    }

    public bool IsAdminMenuVisible
    {
        get { return user.IsInRole("Administrator"); }
    }
}

这是一种更面向对象的设计,并且同样可以接受,因为视图模型实际上并不向视图公开底层规则,并且与控制器一样可测试。就像我上面所说的,这更多的是个人喜好问题,以及您是希望 ViewModel 是智能的(如在 MVVM 中)还是只是愚蠢的 DTO(更多的 MVC 风格)。

【讨论】:

  • 是 - DTO。关于不良设计的要点。都是真的!每个安全相关的财产,例如CanDeleteItems,说明属性将用于什么,从而产生更好的剃须刀代码。并且视图模型仅包含特定于视图的属性,而不是非常通用的 IsAdmin 属性。正如你所说,这导致了维护方面的改进,这在设计方面是一个很好的答案! 10/10!我会选择这个作为答案,但 Queti 已经回答了有关使代码更安全的问题。非常感谢,这很有帮助!
  • @Aaronaught 很好的答案,但是当视图模型被传递回操作方法时,您的解决方案不包括质量分配。如果您有十几个不同的角色,则必须在控制器中再次进行大量检查,然后才能设置更新或创建字段。你如何从控制器方法中抽象出这个逻辑?
【解决方案2】:

如果您的 ViewModel 被传递到您的操作方法中并且变量分配是通过模型绑定完成的,那么是的,它可能会被 Mass Assignment Vulnerability 劫持。如果这是一个问题,我会使用任何其他方法将数据放入您的视图中。

【讨论】:

  • 如果安全性从控制器传递到视图,这不是问题。在视图中,您可以比较和显示您想要的内容。仅当您从视图发送回控制器时,问题才存在。这不应该被双向绑定。如果操作正确,使用具有 IsAdmin 等属性的 ViewModel 是完全安全的。
【解决方案3】:

无论您使用哪种方法,您都将使用User.IsInRole 来确定是否应该显示/隐藏“东西”。

除了您描述的两种方法(设置 ViewModel 属性或直接在视图中使用 User.IsInRole)之外,我能想到的唯一其他传递值的方法是使用 ViewBag、ViewData、Session、Cookie .....所有这些方法几乎都一样。

我是如何做到的:

在我的视图开始时,我使用User.IsInRole,然后将值保存到本地属性中,然后在页面中进一步检查以显示/隐藏内容。

【讨论】:

  • 从上面的 cmets... 由于您的逻辑在视图中,因此您正在避免批量分配漏洞。但是有什么方法可以绕过视图中的本地属性,即出于恶意目的设置此值? (我看不出逻辑和属性是如何在服务器端执行的)。
  • 不,没有人能够以任何方式设置本地属性的值。请注意,当您显示/隐藏“东西”时,请确保您正在阻止 HTML 的呈现,而不仅仅是设置 CSS 类/样式来“隐藏”HTML。您要确保隐藏的 html 不会到达客户端。
  • 正如我所想(和希望的那样)。正在确认。谢谢。
猜你喜欢
  • 2015-08-26
  • 2016-07-28
  • 1970-01-01
  • 2013-01-20
  • 1970-01-01
  • 2017-11-29
  • 1970-01-01
  • 2013-11-10
  • 1970-01-01
相关资源
最近更新 更多