Laravel Form Validation is great, except that it doesn't filter out extraneous keys when array notation is used.
I am type hinting the form request in my controller.
public function saveEdit(Post\EditRequest $request)
{
$valid = $request->validated();
}
If my form has address_1
and address_2
, and the user spoofs address_3
, the spoofed value will not turn up in $valid
.
However, if my form uses array notation, such as venue[address_1]
, and venue[address_2]
, then the user can spoof venue[address_3]
, and it will turn up in $valid
.
Has anyone else come across this? How did you deal with it?
It seems like the validated()
method on the form request class needs to operate recursively.
Unfortunately, Laravel doesn't include built-in support for validating array keys yet, only array values. To do so, we need to add custom validation rules.
However, with alternative protection like $fillable
model attributes and the array_only()
helper function, creating these rules is a lot of extra work for a very unlikely edge case. We provide validation feedback for expected user error and sanitize to protect our data from unexpected input.
If the user spoofs a key, as long as we don't save it, or we filter it out, they won't be too concerned if they don't see a pretty validation message—it doesn't make sense to validate a field that shouldn't exist in the first place.
To illustrate how sanitization works, here's a Venue
model that declares its fillable attributes:
class Venue extends Model
{
protected $fillable = [ 'address_1', 'address_2' ];
...
}
Now, if a malicious user attempts to spoof another attribute in the array:
<input name="venue[address_1]" value="...">
<input name="venue[address_2]" value="...">
<input name="venue[address_3]" value="spoof!">
...and we use the input array directly to update a model:
public function update(Request $request, $venueId)
{
$venue = Venue::find($venueId);
$venue->update($request->venue);
...
}
...the model will strip out the additional address_3
element from the input array because we never declared it as a fillable field. In other words, the model sanitized the input.
In some cases, we may need to simply sanitize array elements without using Eloquent models. We can use the array_only()
function (or Arr:only()
) to do this:
$venueAddress = array_only($request->venue, [ 'address_1', 'address_2' ]);