【问题标题】:Is mass assignment protection needed when properly using validation?正确使用验证时是否需要批量分配保护?
【发布时间】:2021-12-30 18:35:54
【问题描述】:

我第一次在我负责维护的遗留应用程序中使用 Laravel (5.4) 和 PHP,在过去的几个月里,我注意到了一个痛点,那就是批量分配.

我相信我对“是什么”有足够的了解,但我不确定我是否真正正确地利用了它。 The official documentation 使用用户角色的(好的,但极端的)示例来说明问题。我理解这一点的重要性以及为什么我应该保护这些变量。但是,总的来说,我认为我对确定要保护或不保护哪些字段(如果有的话)并没有真正的了解。

似乎 Laravel 的许多不错的功能似乎并没有考虑到质量分配,这是我感到沮丧的主要原因。例如,如果我有一个包含一些可选字段的端点(即您不能在请求中指定它们),那么执行 mymodel->update(request->all()) 只会更新您提供的字段。如果许多字段都受到保护,如果我想实现相同的行为,我将不得不进行多次重复的 isset() 检查,这似乎是不必要的。 我也知道request->all()真的不应该这样用,但我只是想说明一点

当您考虑到您可能会使用强大的验证以及 request->only([...]) 之类的东西来确保您的数据正确并仅过滤到您期望的内容时,这会变得更加麻烦。

所以最终我要问的是:

在存在强大的验证和输入整形方法的情况下,质量分配几乎还有什么值得做的吗?总的来说,当我的验证步骤似乎已经解决了这个问题时,我似乎无缘无故地跳过了这么多圈。 如果它仍然值得做,我错过了什么?批量分配保护是否应该只委托给超级重要的领域,比如用户的角色,而不是其他?

【问题讨论】:

  • 如果您使用正确的验证,您可能有一个$rules 数组。我总是根据这个数组中的键更新我的所有字段。这意味着我接受:$request->only(array_keys($rules)),我总是只更新经过验证的字段(而不是来自请求的一些随机参数)。正如你所说,我只会保护极其重要的属性,比如用户角色、ID、密码,也许是某种支付信息或令牌。
  • 一个好问题要问:+1

标签: php laravel laravel-5


【解决方案1】:

其实这句话我没看懂你的意思:

如果许多领域都被守卫,我将不得不重复许多 isset() 检查我是否想实现相同的行为

不是 Mass-Assignable 字段,只是在请求中出现时被丢弃

但是在用户从请求中传递forbidden_param 之类的参数并且forbidden_param 不可批量分配(要么不在可填充项中,要么在受保护的情况下)的情况下,会有没有抛出错误,eloquent丢弃 forbidden_param

如果你的代码中有这个:

if(isset($request->forbidden_param))

你可以从他们那里得到解脱,当使用质量分配时。

Laravel 让你随心所欲地完成工作

Laravel 不会强迫你使用 helpersFacadeDI ,...来使用服务。

为了保护模型字段,您可以使用不同的解决方案:

1.Eloquent 保护(Mass Assignable)

您可以从请求中传递参数,然后保护它们免受 eloquent 的影响。

//in controller
MyModel::update($request->input())

// in model

$fillable = [ ... ]
//or
$guarded = [ ... ]

这是一种常见的模式,我见过很多使用这种场景的代码。

好处是:

  • 倾斜控制器
  • 如果您只是忘记控制其他地方的输入(控制器/验证),不必担心易出错的字段

2.控制器保护

我看到的另一种模式是将确切的参数放在控制器中的 update 方法中。

Model::update([
    'permitted_param1' => $request->permitted_param
    'permitted_param2' => $request->permitted_param2
]);

在这种情况下,可以通过以下方式放宽模型中可分配的质量:

$guarded = [];

或者可以从中受益,以防另一个控制器上的另一个队友可能忘记控制输入并使用$request->input()。 不过,这将是可选的。

这样做的好处:

  • 能够拥有不同的输入名称数据库字段名称
  • 能够在输入模型之前操作输入(可以使用 mutators 或 repository 来实现)。但如果要更改的字段很少,则实施起来会更简单。

在下面的示例中,控制器使用混合方法:

User::create($request->except('password') + [ 'secret' => bcrypt($request->password)] );

3.在验证器中保护

我见过的最有前途的方法之一是仅使用经过验证的数据,如下所示: 对于 Laravel 8+:

MyModel::update($request->safe()->all());

对于 8 之前的 Laravel:

MyModel::update($request->validated())

你可以放松质量分配

好处:

  • 您已将验证和敏感数据合并到一个位置
  • 更精简的控制器
  • 您确定您已验证所有数据

注意:如果是可选数据,您可以不使用required 规则

4.存储库模式

对此的另一种方法是使用存储库模式,这会给小型项目带来复杂性。

但结果:

  • 更精简的控制器和模型
  • 在存储库中移动查询逻辑
  • 使查询更加可重用和解耦

对于使用此设计模式的项目,此控件可以移动到存储库。

【讨论】:

  • 感谢您的详细回复 - 绝对为我解决了一些困惑。我想我过于重视基于模型的守卫,并且一直认为它始终是正确的选择。关于你关于我的陈述的问题,我指的是重复的isset 检查我必须做 if 我想明确更新一个字段,假设它是受保护的。例如,如果在请求中未设置 guardedVarmyModel->guardedVar = request->guardedVar 将用 null 覆盖该值。显然,在您列出的所有选项中,我错过了一些东西。
猜你喜欢
  • 2021-06-02
  • 2017-11-24
  • 2010-11-19
  • 1970-01-01
  • 2011-11-07
  • 2013-10-12
  • 2014-06-06
  • 2021-12-05
  • 1970-01-01
相关资源
最近更新 更多