I have been and nowadays may be almost every Django Framework users using Django Rest Framework for creating REST APIs. I am using it with token authentication using django-rest
I have found the solution after customized Django REST Throttling,
Its Blocking particular user after 3 login attempts (Block user_id that presents in my application). Block IP address after 6 login attempts for anonymous user.
prevent.py:-
#!/usr/bin/python
from collections import Counter
from rest_framework.throttling import SimpleRateThrottle
from django.contrib.auth.models import User
class UserLoginRateThrottle(SimpleRateThrottle):
scope = 'loginAttempts'
def get_cache_key(self, request, view):
user = User.objects.filter(username=request.data.get('username'))
ident = user[0].pk if user else self.get_ident(request)
return self.cache_format % {
'scope': self.scope,
'ident': ident
}
def allow_request(self, request, view):
"""
Implement the check to see if the request should be throttled.
On success calls `throttle_success`.
On failure calls `throttle_failure`.
"""
if self.rate is None:
return True
self.key = self.get_cache_key(request, view)
if self.key is None:
return True
self.history = self.cache.get(self.key, [])
self.now = self.timer()
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()
if len(self.history) >= self.num_requests:
return self.throttle_failure()
if len(self.history) >= 3:
data = Counter(self.history)
for key, value in data.items():
if value == 2:
return self.throttle_failure()
return self.throttle_success(request)
def throttle_success(self, request):
"""
Inserts the current request's timestamp along with the key
into the cache.
"""
user = User.objects.filter(username=request.data.get('username'))
if user:
self.history.insert(0, user[0].id)
self.history.insert(0, self.now)
self.cache.set(self.key, self.history, self.duration)
return True
view.py:-
from .prevent import UserLoginRateThrottle
....
....
....
class ObtainAuthToken(auth_views.ObtainAuthToken):
throttle_classes = (UserLoginRateThrottle,)/use this method here your login view
def post(self, request, *args, **kwargs):
....
....
settings.py:-
# Django-rest-framework
REST_FRAMEWORK = {
...
...
...
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.UserRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'loginAttempts': '6/hr',
'user': '1000/min',
}
}
As you have stated, you cannot have an authentication system like JWT protect your pages like login and registration. However there are many other things you can do. Below I have mentioned two of them briefly to get you started and rest you can study in detail.
Some browsers have the ability to block content that appears to be an XSS attack. They work by looking for JavaScript content in the GET or POST parameters of a page. If the JavaScript is replayed in the server’s response, the page is blocked from rendering and an error page is shown instead. The X-XSS-Protection header is used to control the operation of the XSS filter.
Implementation
Django provides middleware and settings added in settings>base.py Middleware:
django.middleware.security.SecurityMiddleware
Settings:
SECURE_BROWSER_XSS_FILTER = True
This sets header to X-XSS-Protection: 1; mode=block
Other things you can do to prevent some script from hitting your login or registration pages repeatedly is -
Security Issue
An automated programme may attack to hack username and password of a user or to slow down the server.
These attacks generally take one of a few forms: 1. One IP address trying one username with many passwords. 2. Many IP addresses trying one username with many passwords. 3. One IP address trying many usernames with a few common passwords. 4. Many IP addresses trying many usernames with one or a few common passwords. 5. Attacking on any random url on domain to slow down the server response.
Implementation
Django Rest Framework provides inbuilt settings for throttling
REST_FRAMEWORK = {
...
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle',
'rest_framework.throttling.ScopedRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'anon': '60/minute',
'app1': '10000/day',
'app2': '10000/day',
},
...
}
Another solution is django-defender or django-ratelimit for preventing only for failed login attempts.
Hope it helps.