Ad

Laravel - Rate Limiter Not Bound To User

- 1 answer

I've encountered a pretty weird problem if I say so, a user of an API is reporting that the rate limit of the API is exceeded, when checking this, it seems that the rate-limit is not bound to a specific user, but rather to all users at once.

So when user 1 does an request, the rate-limit for other users will get lowered too.

I've tested this using Postman whilst using two separate Bearer tokens (generated for two unique users)

Does anyone have an idea?

Ad

Answer

Laravel’s built in throttle middleware bases the limitation on the user’s ip address.

see https://github.com/illuminate/routing/blob/master/Middleware/ThrottleRequests.php

protected function resolveRequestSignature($request)
    {
        if ($user = $request->user()) {
            return sha1($user->getAuthIdentifier());
        }

        if ($route = $request->route()) {
            return sha1($route->getDomain().'|'.$request->ip());
        }

        throw new RuntimeException('Unable to generate the request signature. Route unavailable.');
    }

This would explain why you are seeing the limitations you are in your local test. Depending on your end user's use case, it likely explains their issues as well. Are one or more person testing it with different usernames but from the same physical location?

If you think about what the goals of a throttle like this are, this makes good sense. The use case of the throttle starting over when the same end user start using a new username to try to log in only really makes sense in a testing scenario, and is problematic in production environments.

If you do want to alter this behavior, you could make a new custom middleware in the folder app/Http/Middleware which extends the base Illuminate\Routing\Middleware\ThrottleRequests.

Within your custom middleware, you could then override the method resolveRequestSignature() and change it from ip to something else.

You don’t have an authenticated user to work with, so your choices are limited.

If you have users passing in an api key header, that is a reasonable option. Depending on how you have that set up, it would look something like this:

<?php

namespace App\Http\Middleware;

use Illuminate\Routing\Middleware\ThrottleRequests;

/**
 * Class ApiKeyBasedThrottleRequests  
 *  
 * 
 *
 * @package App\Http\Middleware
 */
class SessionBasedThrottleRequests extends ThrottleRequests
{
    /**
     * Override the default, which returns as signature
     * sha1($route->getDomain().'|'.$request->ip());
     *
     * Resolve request signature.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return string
     *
     * @throws \RuntimeException
     */
    protected function resolveRequestSignature($request)
    {
        if ($user = $request->user()) {
            return sha1($user->getAuthIdentifier());
        }

        if ($route = $request->route()) {
            return sha1($route->getDomain().'|'. $request->header('api-key'));
        }

        throw new RuntimeException('Unable to generate the request signature. Route unavailable.');
    }
}

You would then need to update the reference to the middleware in the file app/Http/Kernel in the array $routeMiddleware. Change the value assigned to the key throttle to point to your new custom middleware, App\Http\Middleware;\ApiKeyBasedThrottleRequests::class instead if \Illuminate\Routing\Middleware\ThrottleRequests::class

If you don't have an api key to work with you could think also about session id, but if a single user is trying different usernames from the same browser session, you will still be having the same problem. In that scenario, you could advise the user to restart their browser and try again. However, this basically creates a workaround to the intent of the throttle.

Think carefully and tread lightly with how you approach this. While it makes sense to make things flexible so you don’t block users, keep in mind one of the intents of the throttle is to stop potential malicious users from brute force attacks to discover working username/password combinations. Anything you do here could give them a potential workaround.

Ad
source: stackoverflow.com
Ad