【问题标题】:Where should I generate CSRF form tokens and CAPTCHAs in an MVC application?在 MVC 应用程序中,我应该在哪里生成 CSRF 表单令牌和验证码?
【发布时间】:2018-10-21 22:24:18
【问题描述】:

给定:

我创建了HashCaptcha 类。 Hash 创建表单标记。 Captcha 使用 Graphics 类来创建图像。自定义会话服务包装类用于处理$_SESSION 超全局数据结构。

场景:

我使用依赖注入容器。因此,我想知道在哪里为可公开访问的 HTML 表单定义 CaptchaHash 的实例化。

假设 1: 您应该将 HashCaptcha 注入到 Model 的子代中,因为访问 $_SESSION 需要存储(根据 HTTP 请求)和验证(根据 HTTP响应)CSRF 令牌和验证码答案。视图不应该访问会话数据结构。

假设 2: 您应该将 HashCaptcha 注入到 View 的子代中,因为生成 CSRF 令牌和验证码实际上是 View 的 表示逻辑的一部分,即使它们的验证发生在Model 中。允许从 View 直接或间接访问会话数据结构。

第一个假设似乎是答案,但我想确定一下。

抽象例子:

$session = new Session(); // A custom wrapper class
$hash = new Hash();
$graphics = new Graphics();
$captcha = new Captcha($graphics);


$model = new ContactModel($session, $hash, $captcha);

$view = new ContactView($session, $hash, $captcha);

【问题讨论】:

    标签: php model-view-controller captcha csrf-token


    【解决方案1】:

    注意#1:我认为你应该通读this post。我将强调这个答案的核心部分:“模型不是一个类或任何单个对象”

    注意 #2: IMO 这个问题是 Web MVC 实现固有的问题。第一部分是我将如何解决这个问题;下面从概念上描述为什么会出现这个问题。

    我的回答是:都不是

    MVC架构分为2层:model层和presentation层。服务对象(这些位于模型层中)应将相关的验证码信息实例化/保存到客户端状态。由于这很可能是一个 Web 应用程序,因此表示层(特别是视图对象)将需要从模型层请求此客户端状态。这是否通过服务对象或专门为客户端状态响应量身定制的特定于表示的数据访问对象发生取决于您的实现和最适合。在这里,在视图中,我将通过另一个数据访问对象或(Cookie/Session)Response abstraction* 将客户端状态保存到会话中。通过这种方式,视图不会“访问”会话数据结构,而是利用一个组件(表示层 DAO 或(Cookie/Session)Response抽象*)来完成这项工作。虽然我们不认为 cookie/sessions 是一种表现形式(“它们不是可视的”),表现层负责响应,cookies/sessions 就是这样。

    我很难具体推测这将如何在您的框架中进行,因为似乎存在模型层的合并。您的 ContactModel 肯定希望哈希/验证码来保存客户端状态。您的视图不需要这些,但需要一个额外的机制来以 cookie/会话的形式“响应”客户端状态对 HTTP 请求。

    * Symfony 通常对此很有帮助。我同时使用了表示层 DAO 和 Symfony HttpFoundation 对象,这可能是多余的。

    概念上:会话作为状态与响应

    您在这里解决的基本问题是特定于 Web MVC 实现的。 Web MVC 中的表示层处理“请求”(委托给控制器)​​并呈现“响应”(由视图决定)。在 HTTP 中,cookie 和会话随请求一起发送并在响应中更改。这嵌入在 HTTP 请求/响应性质中。

    如果我们将会话/cookie 保存在模型层中,我们将使模型层依赖于 HTTP,并允许它“响应”请求。即使我们抽象了超全局变量和“注入”抽象(如 Symfony cookie/会话对象),也存在对请求/响应实现的 cookie/会话的内在依赖。您可以在模型层中完成这一切,但我不想这样做。换句话说,虽然模型 负责跟踪状态,但 cookie/会话状态实际上是对请求的响应。

    “但这是有漏洞的逻辑,因为你在表示层中保存状态”

    我认为您可以将所有方法都视为有些“漏洞”,这只是最具凝聚力的方法。如果您将 cookie/会话视为状态,那么我们肯定会在表示层中“保存”状态。如果您将它们视为响应,它们确实在 MVC 眼中,那么这根本不是泄漏的。我认为在模型层发送响应(保存会话/cookie 状态)更糟糕。

    【讨论】:

    • 感谢您投入大量精力提供答案、见解和意见。在我的例子中,令牌和 CAPTCHA(base64 编码)解析为最终嵌入到 HTML 模板中的字符串。这些字符串可以作为数据传递给视图(就像其他数据一样)。我知道您反对在模型中跟踪请求/响应状态,但是这样看黑板是不是太仔细了?可以说这一切都在业务逻辑的保护伞下——一种约束。
    • 我认为视图访问这些字符串,然后响应 (1) 正确的 http 正文和 (2) 正确的 http 标头(cookie/会话)。
    • 我的意思是表单令牌和验证码响应值的最终目的是阻止正常的业务逻辑执行,除非这些值经过验证。在第一次绘制页面时,Model 说,“Here View,获取这些字符串。将它们放在正确的位置。这就是你需要知道的全部内容。”
    • 我不明白为什么这会泄漏模型中的业务逻辑。使用依赖于业务模型的数据进行响应是模型的工作。除了验证码是您业务的一部分之外,我认为您的最新评论没有任何问题。
    • 所以你支持模特?在模型中生成表单标记和验证码字符串的一个优点是整个生命周期存在于一个影响范围内:生成和验证。显然,隐藏/查看它们留给视图。生成表单令牌和 CAPTCHA 值应仅在第一次绘制和验证失败时发生。如果您在模型中保留此位,您可以在确定需要时生成令牌和/或验证码值。
    【解决方案2】:

    答案:您应该在“模型”中生成 CSRF 令牌和 CAPTCHA 值。

    原因:这些值的目的是作为对您的业务逻辑的约束,而不仅仅是在“视图”中显示值或控件。如果“视图”生成表单令牌和验证码挑战值,则必须将它们传回“模型”(一种或另一种方式)。这不合时宜。

    虽然假设二是可行的,但它赋予 View 的职责是它没有完全完成。具体来说,View 永远不会负责验证表单令牌或 CAPTCHA 答案。

    视图可以以虚拟方式将表单标记和 CAPTCHA 质询插入 HTML 表单模板(例如)。这是真的,但是现在“视图”将负责将答案保存到某种持久存储中,而“模型”只会检索和验证它们。

    因此,会话存储就像“视图”和“模型”之间的后台通道,因此将它们耦合(即使使用某种会话服务包装器)。 “视图”不应该通过“模型”传递数据,但如果允许“视图”生成表单标记和 CAPTCHA 答案值,这实际上就是发生的情况。

    这就是我的答案。欢迎大家批评指正。

    【讨论】:

    • 我同意你在这里所说的,但我对会话存储组件感到困惑。模型不应该仅仅为了让视图读取它而将任何内容保存到会话中(我知道你同意这一点)。这不是会话的用途;这样做与定义全局相同。我没有意识到您在询问使用全局在模型层和视图对象之间传输数据的有效性 - 是吗?
    • 同意。在初始 HTTP 请求时,“模型”将通过标准消息传递(以及需要显示/传输的其他数据)将表单令牌和验证码挑战值传递给“视图”。 “模型”应该负责会话存储的输入和输出。
    • “模型应该负责会话存储的输入和输出”是我们不同意的地方。会话存储与有状态的客户端有关。客户端交互通过控制器并由视图通知。 IMO 控制器应仅在从会话存储中读取令牌/任何内容后将其传递给服务对象(模型层)。视图对象将设置会话(因为它们嵌入在 HTTP 响应中),但它们不一定知道这是什么;只是它是响应的一部分。他们知道如何处理这种反应。我希望我的答案能得到这个
    • @jeremy 可能是您的经历与我不同,因为我避开了所有框架并进入了根本——面向对象的设计模式。也就是说,允许会话存储成为“视图”和“模型”(朝这个方向)之间的桥梁将打破观察者模式——观察“模型”的地方,因此在“视图”中更新。您有效的建议是让“视图”(在 HTTP 请求期间)在表单提交期间以延迟方式(从先前的请求,通过会话存储)通知“模型”。这样“模型”可以测试提交的值。
    • 不,我不建议这样做。会话存储绝不应该成为视图和模型之间的桥梁。请告诉我你在哪里看到我这么说。我直接反驳了上面的这个想法:“这不是会话的用途;这样做与定义全局相同。”同样,视图绝不应告知模型会话状态。绝不。我也从来不说这个。我同样使用“面向对象的设计模式”,除了 Symfony、Auryn 等的组件之外没有任何内置框架......
    猜你喜欢
    • 2021-02-27
    • 2013-05-31
    • 2020-03-12
    • 2021-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-24
    相关资源
    最近更新 更多