115 lines
5.0 KiB
ReStructuredText
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/openedx/edx-platform/blob/cd6064692681ab99912e3da3721cd857a0b313e9/common/djangoapps/student/models.py#L980
|
|
.. _cloudflare rate limiting: https://www.cloudflare.com/rate-limiting/
|