Search code examples
laravellaravel-livewire

Is the Livewire checksum secure in this scenario


I am building a fairly large site which utilises Laravel Livewire in a bunch of places, including some sensitive components such as Account Update.

I want to make sure I am not missing any obvious security holes.

Say I have a Livewire component like so:


class AccountInformation extends Component
{
    /**
     * @var array
     */
    public $form_data = [
        'first_name' => '',
        'last_name' => '',
        'email' => '',
        'telephone' => '',
    ];

    /**
     * @return void
     */
    public function submit()
    {
        $this->validate();

        auth()->user()->update($this->form_data);
    }
}

Let's also assume that my User model has none of its properties guarded in the model.

Is the Laravel checksum enough security to ensure a hacker cannot pass 'id' into the request and update a different user's account?

For example, I could send a curl request like so:

{
   "fingerprint":{
      "id":"xxxxxxxx",
      "name":"account-form",
      "locale":"en",
      "path":"customer/account-form",
      "method":"GET",
      "v":"acj"
   },
   "serverMemo":{
      "children":[
         
      ],
      "errors":[
         
      ],
      "htmlHash":"1111111",
      "data":{
         "form_data":{
            "id":10000,
            "first_name":"Bob",
            "last_name":"Sith",
            "email":"naughty@hacker.com",
            "telephone":"999"
         }
      },
      "dataMeta":[
         
      ],
      "checksum":"9fa4a09176237dc7224fe5e0d9d9656ccd8c88e4007f17008db000ffbb93f2fd"
   },
   "updates":[
      {
         "type":"callMethod",
         "payload":{
            "id":"jzoh",
            "method":"submit",
            "params":[
               
            ]
         }
      }
   ]
}

So in the above request I have added in an "ID" field to the form_data. Without any kind of tamper check, this would update the user with the id 100000.

In the Livewire docs and also in my testing, Livewire throws an error as the form fails the checksum test, but I just want to be absolutely certain there is nothing more I need to do to secure a livewire component from such 'tampering'?


Solution

  • The checksum will always validate that the data that was dehydrated from the previous response, would not be changed when rehydrating the following request. This means you cannot tamper with the data in serverMemo.data, simply because the checksum you pass in with the request will not match with the checksum being generated on hydration based on the data being passed in the request.

    There is no way to generate and pass in a new checksum that would match with the tampered data, because the checksum is generated using the internal encryption with sha256 (here is the exact code that generates the checksum, in Livewire\ComponentChecksumManager).

    However, you ALWAYS need to do your own backend validation based on the requests coming in, as with any type of HTTP request. To tamper with data in the Livewire component, you can pass in the updates property that would sync input via the syncInput type, for example like this,

    "updates":[
       {
          "type": "syncInput",
          "payload": {
             "id": "y6gi",
             "name": "form_data.id",
             "value": "10000"
          }
       }, 
       {
          "type":"callMethod",
          "payload":{
             "id":"jzoh",
             "method":"submit",
             "params":[]
          }
       }
    ]
    

    But this would be no different to calling Livewire.find('<component-id>').set('form_data.id', 10000) from the browser-console before calling the submit method. Hence why you absolutely must do your own validation and authentication backend.