Home > database >  How to prevent a Laravel security leak where an API returns not found when you are not authenticated
How to prevent a Laravel security leak where an API returns not found when you are not authenticated

Time:11-23

Lets say I have the following route to display a specific user in an API point, that is protected via some authentication middle:

Route::get('/v1/user/{user}', 'Api\V1\UserController@show')->middleware('can:show,user');

Assume my database is filled with just a handfull records. Then, going to the route without a credential:

  • /api/v1/user/1 will give me 403 UNAUTHORIZED
  • /api/v1/user/999999 will give me 404 NOT FOUND

In this setup, any visitor can find out how many users I have, by just poking around and trying some routes. Moreover, they can also find out the primary identifier (the id) of these users. This is something I would like to hide from a business perspective, but also from a security perspective.

An appoach that partially addresses this issue, is using UUID's. UUIDs are universally unique alpha-numeric identifiers that are 36 characters long, and Laravel supports the use of these on your models. This will hide the amount of records our have, and make it hard to find existing records. However, since it is still statistically possible to find records by just brute forcing, I feel this is not the correct answer to this problem.

So, how can I prevent a Laravel security leak where API returns not found when you are not authenticated?

CodePudding user response:

you can use this package to generate a slug. Then you can use the slug instead of the id.

https://github.com/spatie/laravel-sluggable

CodePudding user response:

You can follow GitHub's technique and return a 404 (Not found) instead of 403 (Unauthorized).

This way, attackers don't know if the resource actually exists or not.

To achieve this in Laravel, you may do it like this: In app/Exceptions/Handler.php, create/edit the method called render() and check if the status code is 403. If so, throw a 404 instead.

public function render($request, Throwable $e)
{
    if ($e->getStatusCode() === 403) {
        abort(404);
    }

    return parent::render($request, $e);
}

If you want to test it, just add this test route in routes/web.php:

Route::get('/test', function () {
    return abort(403); // should return a 404
});

Resources:

  • Related