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
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 thewire: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.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 thesyncInput
type, for example like this,But this would be no different to calling
Livewire.find('<component-id>').set('form_data.id', 10000)
from the browser-console before calling thesubmit
method. Hence why you absolutely must do your own validation and authentication backend.