Files
edx-platform/docs/decisions/0009_simplify_ratelimiting.rst
Feanil Patel 8925756f11 doc: Add some docs for how rate-limiting works. (#26475)
* doc: Add a decision around rate-limiting in edx-platform

While looking into whether a change to how we handle RateLimitExceptions
would impact logins, I learned the following about the current state of
how rate limiting works in our login flows.  Based on what I learned, I captured
the current state as well as decisions on how we should rate limit in the future.
2021-02-17 13:16:57 -05:00

115 lines
5.0 KiB
ReStructuredText

Simplify Login and Other Rate Limiting
======================================
Status
------
Accepted
Decisions
---------
* We will deprecate and remove the `django-ratelimit-backend`_ from
edx-platform. This library is currently not being actively developed and is
looking for a new maintainer. It is also very specific to rate limiting the
authentication backend and so can't easily be applied more generally.
* For rate limiting in pure django views, we will use the `django-ratelimit`_
library. This library is well built for general use and can be easily used
multiple times for stacked rate limiting over multiple keys. eg. limit by IP
or by user name.
* For rate limiting in any DRF based views, we will use the
`djangorestframework rate limiting`_ capabilities that are built in to the
framework.
Context
-------
edx-platform currently uses multiple different ratelimiting tools which can
lead to confusion and difficulty understanding how endpoints are secured.
Consider the following case study in how our login endpoints are currently rate
limitied.
Rate limiting Logins
~~~~~~~~~~~~~~~~~~~~
1st party vs 3rd party login
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
edx-platform allows for both 1st party auth, where you provide the LMS with
your credentials and it gives you back some session tokens, and also 3rd party
auth, in which you are directed to a 3rd party to authenticate and then
redirected to the LMS with a token from that third party which is exchanged for
1st party(LMS) session tokens.
Login User View
^^^^^^^^^^^^^^^
The ``login_user`` view in ``views/login.py`` is called for both 1st and 3rd
party login flows. In the 3rd party login flow, it's called as the callback
when redirected from the 3rd party login back to the LMS.
Currently this view is accessed through two different endpoints. It is
currently a pass-through call for the ``/api/user/v1/account/login_session``
using a DRF view. It is also called directly at ``/login_ajax``
Currently there are five different rate limiting implementations in use as a
part of the login flow.
* `django-ratelimit`_ - A 3rd party rate limiting library that allows you to
decorate any view to add rate limiting functionality.
The ``ratelimit`` decorator on ``login_user`` uses the
LOGISTRATION_RATELIMIT_RATE django setting to determine the rate limit. The
default is ``100 requests per 5 minutes`` This applies to both 1st and 3rd
party login attempts.
* `django-ratelimit-backend`_ - Another 3rd party rate limiting library built
specifically to rate limit login views. It provides a mixin that we can add
to an existing auth backend. This replaces the base implementation of the
``authenticate`` class with one that will also track the rate limits for that
call.
We currently use the default rate limits provided by the upstream library
which is ``30 requests per 5 minute`` This is only applies to 1st party
login attempts and does not count against 3rd party logins.
* `djangorestframework rate limiting`_ - DRF provides a throttling
capability that can be used with any DRF views. This is not used for the
login view but is applied to the ``/third_party_auth_context`` endpoint
(``TPAContextView``) to rate limit 3rd party auth requests. The default
rate limit for this endpoint is ``20 requests per minute``. Since this rate
limiting is only applied to a 3rd party auth specific view, it only impact
the rate at which 3rd party auth is possible.
* `MAX_FAILED_LOGIN_ATTEMPTS`_ - This is an optional feature that can be
enable(disabled by default) which will limit the number of failed logins a
user is allowed to have before their account is locked out. This feature
works slightly differently from the other rate limiting features in that it
persists the number of failures and does not reset them until we have had a
successful login. If a maximum number of failed request is reached, the
account is locked out for 30 minutes. The default settings for this feature
are to lock out the user for 30 minutes if 6 login failures occur over any
period of time.
edx.org has the ``MAX_FAILED_LOGIN_ATTEMPTS`` feature enabled.
* `cloudflare rate limiting`_ - This is edx.org specific and not enabled by
default for Open edX.
Ratelimiting other endpoints
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Other endpoints usually use only one of the above mentioned 3 libraries(drf,
django-ratelimit, django-ratelimit-backend). The decision below should clarify
how and when we should be using different libraries.
.. _django-ratelimit: https://django-ratelimit.readthedocs.io/en/stable/usage.html#usage-chapter
.. _django-ratelimit-backend: https://django-ratelimit-backend.readthedocs.io/en/latest/
.. _djangorestframework rate limiting: https://www.django-rest-framework.org/api-guide/throttling/
.. _MAX_FAILED_LOGIN_ATTEMPTS: https://github.com/edx/edx-platform/blob/cd6064692681ab99912e3da3721cd857a0b313e9/common/djangoapps/student/models.py#L980
.. _cloudflare rate limiting: https://www.cloudflare.com/rate-limiting/