Framework: Bypassing validation by spoofing array input with input name containing dot

Created on 4 Jul 2016  路  5Comments  路  Source: laravel/framework

Let's assume you have input
<input type="text" name="product[name]">

and you have the following code in your controller to validate it and save to db

$this->validate($request, ['product.name' => 'required']);

Product::create([name => $request->input('product.name']);

By changing input name from "product[name]" to "product.name" attacker can save NULL value to db, as $request->input('product.name'] returns NULL for input named "product.name".

Most helpful comment

Seems to be impossible to recreate this issue with regular html form, as PHP server by default converts dots and spaces in variable names to underscores, and it seems like Laravel relies on this feature. But with an "application/json" request type it is possible to bypass this conversion and present Validator with an input containing a dot in a name, which will pass validation. The following code returns "true":

$validator = Validator::make(
    ['product.name' => 'somevalue'],
    ['product.name' => 'required']
);
dd($validator->passes());

To recreate live scenario:
Add validator to your controller

$this->validate($request, [
    'product.name' => 'required',
]);
dd('passes, and product name is ', $request->input('product.name') );

Send ajax "application/json" request with e.g. jQuery ajax:

    $.ajax({
        beforeSend: function (xhr) {
            xhr.setRequestHeader("X-XSRF-TOKEN", 'YOUR LARAVEL TOKEN ENCRYPTED')
        },
        method: "POST",
        url: 'http://yoursite.com/path_to_controller_route',
        contentType: 'application/json',
        data: '{"product.name":"any value here only to pass the required rule"}'
    })

You will get:
"passes, and product name is " null

All 5 comments

But null won't pass the required validation rule, no?

Could'nt reproduce this with

$this->validate($request, ['product.name' => 'required']);

Also tried:

        $validator = \Validator::make($request->all(), [
            'product.name' => 'required',
        ]);

        if ($validator->fails()) {
            dd('fails');
        }

        dd($request->input('product.name').' passes');

Test results:

//As form-data

product[name] => 14
"14 passes"

product.name => 15
"fails"

product.name => 15
product[name] => 14
"14 passes"

product.name => null
product[name] => 14
"14 passes"

product[name] => 14
product.name => 15
"14 passes"

product[name] => 14
product.name => null
"14 passes"

product[name] => null
product.name => 15
"fails"

product.name => 15
product[name] => null
"fails"

product[name] => null
product.name => null
"fails"

product.name => null
product[name] => null
"fails"

If som1 could reproduce this please share an example, looks like input as well as the validator does'nt even read the field sent as product.name, this way it does'nt passes the validation...

Seems to be impossible to recreate this issue with regular html form, as PHP server by default converts dots and spaces in variable names to underscores, and it seems like Laravel relies on this feature. But with an "application/json" request type it is possible to bypass this conversion and present Validator with an input containing a dot in a name, which will pass validation. The following code returns "true":

$validator = Validator::make(
    ['product.name' => 'somevalue'],
    ['product.name' => 'required']
);
dd($validator->passes());

To recreate live scenario:
Add validator to your controller

$this->validate($request, [
    'product.name' => 'required',
]);
dd('passes, and product name is ', $request->input('product.name') );

Send ajax "application/json" request with e.g. jQuery ajax:

    $.ajax({
        beforeSend: function (xhr) {
            xhr.setRequestHeader("X-XSRF-TOKEN", 'YOUR LARAVEL TOKEN ENCRYPTED')
        },
        method: "POST",
        url: 'http://yoursite.com/path_to_controller_route',
        contentType: 'application/json',
        data: '{"product.name":"any value here only to pass the required rule"}'
    })

You will get:
"passes, and product name is " null

@denho , @themsaid
Confirmed, passes with this:

"application/json"
{"product.name":"any value here only to pass the required rule"}
"passes, and product name is "
null

This issue doesn't exist anymore in laravel 5.3, we replace the dot in attribute names with -> before starting any check.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ghost picture ghost  路  3Comments

digirew picture digirew  路  3Comments

SachinAgarwal1337 picture SachinAgarwal1337  路  3Comments

klimentLambevski picture klimentLambevski  路  3Comments

YannPl picture YannPl  路  3Comments