From 8925756f11f6cac2ecbf30e7a4bd42c3b9adb02c Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Wed, 17 Feb 2021 13:16:57 -0500 Subject: [PATCH] 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. --- docs/decisions/0009_simplify_ratelimiting.rst | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/decisions/0009_simplify_ratelimiting.rst diff --git a/docs/decisions/0009_simplify_ratelimiting.rst b/docs/decisions/0009_simplify_ratelimiting.rst new file mode 100644 index 0000000000..c91f5956e3 --- /dev/null +++ b/docs/decisions/0009_simplify_ratelimiting.rst @@ -0,0 +1,114 @@ +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/