Django
Protect login views with middleware and report failed logins with the user_login_failed signal.
Middleware that refuses listed addresses on sensitive paths, and a signal receiver that reports addresses behind repeated failed logins. Both use Django's cache, so a busy site makes at most one lookup per address per hour.
Block listed addresses on login
yourapp/isipsafe.py:
import requests
from django.conf import settings
from django.core.cache import cache
from django.http import HttpResponseForbidden
URL = getattr(settings, "ISIPSAFE_URL", "https://isipsafe.com")
PROTECTED = ("/accounts/login/", "/accounts/signup/", "/admin/login/")
def score(ip):
key = "isipsafe:" + ip.replace(":", "_")
s = cache.get(key)
if s is None:
try:
r = requests.get(URL + "/api/v1/check", params={"ip": ip}, timeout=3)
s = r.json()["data"]["abuseConfidenceScore"] if r.ok else 0
except requests.RequestException:
s = 0 # fail open
cache.set(key, s, 3600)
return s
class IsIPsafeMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if request.path.startswith(PROTECTED) and score(request.META["REMOTE_ADDR"]) >= 75:
return HttpResponseForbidden("Your network address has a poor reputation.")
return self.get_response(request)
Add "yourapp.isipsafe.IsIPsafeMiddleware" to MIDDLEWARE. Behind a proxy, set REMOTE_ADDR from the trusted forwarded header first (for example with a small middleware or your WSGI server's proxy settings).
Report failed logins
import requests
from django.conf import settings
from django.contrib.auth.signals import user_login_failed
from django.core.cache import cache
from django.dispatch import receiver
@receiver(user_login_failed)
def report_failed_login(sender, credentials, request=None, **kwargs):
if request is None:
return
ip = request.META["REMOTE_ADDR"]
key = "isipsafe-fails:" + ip.replace(":", "_")
cache.add(key, 0, 600)
if cache.incr(key) != 10:
return
requests.post(
"https://isipsafe.com/api/v1/report",
headers={"Authorization": "Bearer " + settings.ISIPSAFE_KEY},
data={"ip": ip, "categories": "brute-force,web-app-attack",
"comment": "10 failed logins within 10 minutes on a Django site"},
timeout=5,
)
Put it in a module your app imports at start-up (for example from AppConfig.ready()). For plain Python or Flask, see Python.
Need a hand?
If something here does not work with your version of Django, or you have improved it, tell us. We update these pages with what people send.
Django and its logo are trademarks of their respective owners. isipsafe is not affiliated with or endorsed by them.