Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throttling on token generation/delivery missing #48

Closed
arjan-s opened this issue Aug 5, 2020 · 2 comments
Closed

Throttling on token generation/delivery missing #48

arjan-s opened this issue Aug 5, 2020 · 2 comments

Comments

@arjan-s
Copy link
Contributor

arjan-s commented Aug 5, 2020

The new SideChannelDevice added a generic way to generate, expire and verify tokens that are sent to the user via another channel. There is also a ThrottlingMixin for throttling the verify calls.
Sometimes, like with SMS, every token costs a bit of money. It would be good to add some generic way to throttle the sending of those tokens in order to limit the costs for the website owner in case of an attack.
I think a good place would be the ThrottlingMixin, but I'm not sure what would be the best way to implement it.

@schinckel
Copy link

Indeed. I'm about to implement something that limits a given user to a number of token generation attempts per day to prevent spiralling SMS costs.

@some1ataplace
Copy link

To limit a given user to a number of token generation attempts per day, we can add a new attribute token_generation_per_day_limit to the SideChannelDevice model, which will hold the maximum number of tokens that can be generated per day. We will also add a new method get_token_generation_count() to the model to fetch and return the number of token generation attempts made by the user today.

Here's a code snippet that shows how we can achieve this:

from django.utils import timezone

class SideChannelDevice(OTPDevice, ThrottlingMixin):
    # Maximum number of token generation attempts per day
    token_generation_per_day_limit = 10

    # Override the generate_token() method to add token throttling
    def generate_token(self):
        # Throttle the token generation attempt
        self.check_throttle()

        # Increment the token generation attempt count
        self.increase_throttle_count()

        # Generate the token
        token = super().generate_token()

        # Reset the throttle if it's a new day
        if self.get_token_generation_count() == 1:
            self.reset_throttle()

        return token

    def get_token_generation_count(self):
        today = timezone.now().date()
        return self.throttling.get_daily_use(today)
        #token_attempts_today = self.throttling_counters.get(today, 0)
        #return token_attempts_today

This method fetches the current date and returns the number of token generation attempts made by the user on that day.

We can then use the token_generation_per_day_limit and get_token_generation_count() methods to enforce the daily limit on token generation attempts in the generate_token() method as follows:

    def generate_token(self):
        # Throttle the token generation attempt
        self.check_throttle()

        # Increment the token generation attempt count
        self.increase_throttle_count()

        # Check if the user has exceeded the token generation limit for the day
        if self.get_token_generation_count() >= self.token_generation_per_day_limit:
            raise exceptions.Throttled(
                wait=self.throttling.get_wait_time(),
                detail=f"Token generation limit of {self.token_generation_per_day_limit} per day exceeded"
            )

        # Generate the token
        token = super().generate_token()

        # Reset the throttle if it's a new day
        if self.throttling.reset()

        return token

This code will check whether the user has exceeded the token generation limit for the day and raise a custom Throttled exception if the limit has been exceeded. If the limit has not been exceeded, it will call the generate_token() method of the super class to generate the token and reset the throttle if it's a new day.

By implementing these changes in the SideChannelDevice model, we can limit the number of token generation attempts made by a given user per day.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants