skip to Main Content

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":"[email protected]",
            "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’?

2

Answers


  1. Livewire creates a hash based on the data that gets sent to the frontend, and one based on the data that gets sent back. If someone tampers with the frontend data, e.g. by changing the ID, the hash wouldn’t match and a checksum error would be thrown. If someone adds or removes data, again, the hash wouldn’t match.

    The data processed via wire:model is evaluated separately. There is a caviat. If a user is smart, he might be able to figure out the property that is used to update the User. When changing the wire:model in the frontend, it might allow them to change the data in your form. However, this only is dangerous if you blindly update the User model using the form data. If you only use validated data, then you will only get the properties you’ve chosen to validate.

    $userData = $this->validate();
    auth()->user()->update($userData);
    
    Login or Signup to reply.
  2. 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 LivewireComponentChecksumManager).

    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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search