What is a Laravel Validation Rule Injection? Well first let's look at the following vulnerable code
public function update(Request $request) {
$id = $request->route('id');
$rules = [
'username' => 'required|unique:users,username,' . $id,
];
$validator = Validator::make($request->post(), $rules);
if ($validator->fails()) {
return response()->json($validator->errors(), 422);
}
$user = User::findOrFail($id);
$user->fill($validator->validated());
$user->save();
return response()->json(['user' => $user]);
}
If your eye caught the vulnerability in
'required|unique:users,username,' . $id code
great job!
So what the “unique” rule is doing here, it’s making sure the username is unique inside the users table and it will also ignore the row with the given ID during the check. But the issue here is that we got the $id from the request and without validating it, we used it to create a validation rule based on the user’s input. So using that we can customize the validation rule and create some attack vectors, let's look at the following examples.
1. Making the validation rule optional
The simplest thing that we can do here is to send a request with ID = 10|sometimes
, which will alter the validation rule to required|unique:users,username,10|sometimes
and will allow us to not skip the username in the request data, depending on your application business logic, a bypass like this might create a security issue.
2. DDOS the server by creating an evil REGEX validation rule
Another attack vector here could be to create an evil Regex validation, that is vulnerable to ReDoS attack and DDOS the app.
For example, the following request would consume a lot of CPU and if multiple requests sent concurrently can cause a big CPU spike on the server.
PUT /api/users/1,id,name,444|regex:%23(.*a){100}%23
{
"username": "aaaaa__ALOT_OF_REPETED_As_aaaaaaaaaa"
}
3. SQL Injection
The simplest SQL injection here would be to just add an extra validation rule that is querying the database, for example
PUT /api/users/1,id,name,444|unique:users,secret_col_name_here
{
"username": "secret_value_to_check"
}
But important to mention since using unique we are able to provide both custom column name and values (values are not going through PDO parameter binding) possibilities of SQL injection here could be not limited to just a simple attack vector that is mentioned above. For more details, check out Laravel Blog’s post here.
Prevention tips:
- The best prevention here is to not use user-provided data to create a validation rule
- If you have to build your validation rule based on provided data(ID in our example), make sure to cast or validate the provided value before putting it into the validation rule.
📝 If you have a Laravel application that is running on production, check out also our Laravel applications security testing service.