9

问题

我只是想弄清楚在 Breeze 中保存更改时我需要在服务器端实现多少自己的安全性。特别是,我正在考虑恶意用户如何手动破解 SaveChanges 请求,或破解客户端中的 javascript,以绕过我的正常业务规则 - 例如,恶意更改我的实体上的外键 ID。

我想准确了解我需要将安全工作重点放在哪里;我不想浪费时间实施不需要的安全层。

我在服务器端使用 Breeze 和 .net 和 Entity Framework。

例子

这是一个简单的例子。ObjectA有一个 的引用ObjectB,并且ObjectA归一个特定的User. 因此,我的数据库如下所示:

ObjectA:

Id    ObjectB_Id    SomeField          User_Id
1     1             Alice's ObjectA    1
2     2             Bob's ObjectA      2

ObjectB:

Id    SomeOtherField
1     Foo
2     Bar

User:

Id    Name
1     Alice
2     Bob

从这个模型中,我的安全问题是:

  1. 我不希望未经身份验证的用户更改任何数据
  2. 我不希望 Bob 能够对 Alice 的ObjectA
  3. 我不希望爱丽丝试图将她指向ObjectA鲍勃的ObjectB
  4. 我不希望 Bob 尝试将User_Id他的角色更改ObjectA为 Alice。

(1) 的解决方案是微不足道的;我将确保我的 SaveChanges 方法具有[Authorize]属性。

我可以轻松地使用 Fiddler 构建 SaveChanges 请求来重现问题 2 到 4 - 例如,我可以构建一个请求,将AliceObjectA 更改为指向 Bob 的ObjectB. 消息内容可能如下所示:

"entities":
[
    {
        "Id":1,
        "ObjectB_Id":2,
        "SomeField":"Alice's ObjectA",
        "User_Id":1,
        "entityAspect":
        {
            "entityTypeName":"ObjectA:#MyNamespace",
            "defaultResourceName":"ObjectAs",
            "entityState":"Modified",
            "originalValuesMap":
            {
                "ObjectB_Id":"1"
            },
            "autoGeneratedKey":
            {
                "propertyName":"Id",
                "autoGeneratedKeyType":"Identity"
            }
        }
    }
],

正如我所料,当服务器端没有实现安全性时,这会将更新的值保存ObjectB_Id到数据库中。

但是,我还确认,如果 中没有 for 条目ObjectB_IdoriginalValuesMap那么即使我更改ObjectB_Id消息主体中的值 for ,它也不会在数据库中更新。

一般规则?

所以,我认为这意味着我需要在服务器上遵循的一般安全规则是:

[2013 年 7 月 4 日编辑 - 为清楚起见重写]

一般来说:

  • 消息中的任何内容都不可信:originalValuesMap 中的值和所谓的“未更改”值均不可信
    • 唯一的例外是实体的身份,我们可以假设它是正确的。
    • 假设“未更改”的属性可能已被篡改,即使它们不在 originalValuesMap 中

对于“未更改”属性(不在 originalValuesMap 上的属性):

  • 当“使用”任何“未改变”的属性时,我们不能使用消息中的值;我们必须从数据库中检索对象并使用其中的值。
    • 例如,当检查对象的所有权以确保允许用户更改它时,我们不能信任消息上的 UserId;我们必须从数据库中检索实体并使用其中的 UserId 值
  • 对于我们没有以任何方式使用的任何其他“未更改”属性,我们不必担心它是否被篡改,因为即使被篡改,被篡改的值也不会被持久化到数据库中

对于更改的属性(也在 originalValuesMap 上的属性):

  • 业务规则可能会阻止更改特定属性。如果是这种情况,我们应该对每个这样的规则进行检查。

  • 如果允许更改值,并且它是外键,我们可能应该执行安全检查以确保会话身份允许使用新值

  • 我们不能使用 originalValuesMap 中的任何原始值,因为这些值可能已被篡改

[编辑结束]

实施规则

假设这些规则是正确的,我想有几个选项可以围绕更改的外键实现安全性:

  • 如果业务规则不允许更改特定字段,我将拒绝 SaveChanges 请求
  • 如果业务规则允许更改特定字段,我将检查是否允许新值。在这样做时,不能使用originalValuesMap; 我需要访问数据库(或其他可信来源,例如会话 Cookie)

将这些规则应用于我上面给出的安全问题,

  • 安全问题 (2)。我需要根据当前数据库中的用户身份检查会话中的用户User_ID身份ObjectA。这是因为我无法信任请求中的 User_ID,即使它不在originalValuesMap.

  • 安全问题 (3)。如果业务规则允许更改ObjectB,我将需要检查谁拥有 的新值ObjectB_Id;我将通过从数据库中检索指定的 ObjectB 来做到这一点。如果这ObjectB不属于ObjectA的所有者,我可能想拒绝更改。

  • 安全问题 (4)。如果业务规则允许更改User,这已经包含在 (2) 中。

问题

所以,真的,我正在寻找确认我的想法是正确的。

  1. 我的一般规则是否正确?
  2. 我对规则的执行听起来合理吗?
  3. 我错过了什么吗?
  4. 我是不是把事情复杂化了?
4

2 回答 2

2

菲尔......你在这里绝对是正确的。您在列出问题和威胁以及减轻这些威胁的一般方法方面做得很好。就好像您已经编写了 Breeze 安全章节的介绍……我们还没有写到。

我不认为您“使事情复杂化”

读到这里的人可能会想“哇……工作量很大……Breeze 的东西一定是不安全的”。

嗯,这是很多工作。但让事情变得困难的不是微风。这是存在的每个 Web 应用程序的必要思考。身份验证只是保护应用程序的第一步……最简单的步骤……。

您不应该信任任何客户端请求……即使客户端已通过身份验证。这意味着确保客户端被授权发出请求,并且进入和退出服务器的内容与客户端声称的和被允许做的一致。这些是适用于所有 Web 应用程序的一般原则,而不仅仅是 Breeze 应用程序。在 Breeze 中坚持这些原则并不比在任何其他技术中更难。

您可能忽略了一项微风技术。EFContextProvider.Context应该只持有要保存的实体;不要使用它来检索原始实体。您需要单独DbContext检索原始实体以与来自客户端的变更集实体进行比较。

我们正在研究演示如何处理您描述的问题的示例。例如,我们推荐(和演示)一个“验证规则引擎”,它插入到BeforeSaveEntitiesDelegate; 这种“引擎”方法可以更轻松地编写大量服务器端规则并自动应用它们。

我们的示例和指南尚未准备好发布。但他们正在到来。

同时,按照您在此处描述的本能进行操作。关于你的进展的博客。告诉我们……我们会很高兴突出您的帖子。

于 2013-07-05T02:07:32.837 回答
0

我一直在寻找有关同一问题的指导,我很高兴找到您出色的分析。在我看来,我们的问题的答案是不同的,假设我们正在讨论由多个模块组成并且寿命超过一年的应用程序。

如果规则变得过于复杂,则意味着我们可能使用了不恰当的方法。我相信许多优秀的开发人员会遵守这些规则,但可悲的事实是,我们的大多数同行要么会弄错,要么会在压力下忘记其中的一些。

我想说我们需要回到 Fowler、Evans 和 Nilssons 的出版物,并在他们之后重复一遍,在更大的应用程序中(并且这些应用程序具有很强的安全要求),实体模型根本不应该暴露给客户端(出于安全以外的其他原因 - 例如可维护性)。

另一方面,值得研究对 Greg Young 和 Udi Dahan 后来提出的这些原始想法的修改。这些本质上是说读取模型不必并且通常与写入“数据”的模型不同。

总而言之,我想说基本规则应该是不要使用 Breeze 进行写作并使用它进行阅读(使用 DTO/Projections),前提是您不查询“真实”模型而是专门构建的模型用于阅读(例如视图而不是表格)。

如果您遵循您的领域和用例,尤其是如果您遵循测试驱动的方法,所有这些都会很自然地出现。在遵循测试驱动开发的同时,您真的会最终获得针对业务规则的 BeforeSaveEntities 解决方案吗?

于 2013-12-16T21:52:50.640 回答