Как сделать прогрессивный дроссель скорости входа в Django?

Я работаю над приложением Django/DRF и пытаюсь реализовать API-дроссель, который будет иметь все более длинную задержку для неудачных попыток входа.

Например, блокировать пользователя на 1 минуту после 3 неудачных попыток, 10 минут после 6 неудачных попыток, 30 минут после 9 и т.д., подобно тому, как это делают телефоны и что довольно часто встречается на страницах входа в систему в целом. Я был удивлен, что в Django или DRF не встроен прогрессивный дроссель, учитывая, насколько распространен этот сценарий входа...

Опция дросселя ДРФ:

Django Rest Framework APIView предоставляет поле throttle_classes и метод get_throttles(), и у него есть несколько общих дросселей для выполнения задержки дросселя с фиксированной скоростью . Я могу имитировать прогрессивную скорость, добавив список дросселей, например, так:

def get_throttles(self):
    return [
        MyCustomThrottle('3/m'),
        MyCustomThrottle('6/10m'),
        MyCustomThrottle('9/30m'),
    ]

и затем добавьте пользовательский метод get_cache_key() к MyCustomThrottle, который возвращает уникальный ключ, не пересекающийся с другими дросселями в списке.

Это почти работает - это работает для блокировки бота, который просто держит ногу на газе - однако, у этого есть пара проблем:

  1. Дроссели DRF не имеют простого способа очистить список дросселей, если/когда пользователь успешно вошел в систему. Я вроде как обошел эту проблему, вручную изменив кэш, который используют дроссели DRF, но это не идеально...

    .
  2. Дроссели DRF срабатывают в определенный момент цикла запроса, и аутентификация может произойти или не произойти к этому моменту - поэтому дроссели могут не знать, являются ли входящие учетные данные хорошими:

    A. Если делать аутентификацию через поле APIView.authentication_classes, то аутентификация происходит до дросселей, тогда дроссели могут знать, был ли аутентификация успешной и могут действовать соответственно. Это имеет обратную сторону: каждый запрос бота будет приводить к попаданию в БД.

    .

    B. Если делать auth в коде представления, то auth происходит после срабатывания дросселей. Минус в том, что дроссели не знают, хороши ли входящие креды, но плюс в том, что боты блокируются до того, как БД будет поражена.

    .

Наше приложение использует вариант B, поскольку мы также внедряем 2FA (возможно, есть способ сделать 2FA через authentication_classes, но это то, что есть сейчас...) и потому что мы хотим, чтобы боты блокировались с минимальной нагрузкой на DB. Вариант B вроде как исключает DRF-дроссели, поскольку крайние случаи вызывают отвратительный UX.

Другие варианты:

Я начал рассматривать django-axes в качестве альтернативы DRF-дросселям. Он реализует дросселирование через промежуточное ПО, поэтому он дружественен к аутентификации независимо от того, где/когда происходит аутентификация, и он предоставляет простой способ очистки дросселей на основе пользователя, IP или в целом. Недостатком является то, что в нем нет способа обеспечить несколько дросселей для прогрессивной/возрастающей задержки дросселирования, как я хочу.

Возможно, есть способ заставить django-axes работать, но, независимо от этого, я чувствую, что изобретаю колесо заново... Прогрессивное дросселирование довольно распространено в мире логинов, но, похоже, оно полностью отсутствует в мире Django...

Есть ли что-то простое, что я упускаю? Кажется, что что-то подобное уже должно быть встроено где-то в Django/DRF...

Вернуться на верх