После того, как мы подменили страницу авторизации Django на свою собственную кастомизированную страницу авторизации , настал момент для использования данной подмены для целей улучшения безопасности сайта. Например, внедрение блокировки злоумышленника по IP при попытке подбора пароля.
Предлагаю такой вариант блокировки: при трех неудачных попытках ввода пароля IP блокируется на 15 минут, если такая блокировка на 15 минут происходит 3 раза, то IP блокируется на 24 часа.
Для реализации блокировки понадобится модель, в которой будет находится 4 поля:
- IP адрес;
- Количество попыток ввода пароля;
- Время разблокировки;
- Статус блокировки - True - если заблокирован, False - если не заблокирован.
Сразу покажу результат работы блокировок в админке сайта, за пару месяцев уже накопилась небольшая коллекция.
models.py
А теперь посмотрим, как будет выглядеть модель для временных блокировок от подбора пароля, а также, как настроить админку, чтобы таблица с блокировками выглядела так, как показано на рисунке выше.
- from django.db import models
- from django.contrib import admin
- class TemporaryBanIp(models.Model):
- class Meta:
- db_table = "TemporaryBanIp"
- ip_address = models.GenericIPAddressField("IP адрес")
- attempts = models.IntegerField("Неудачных попыток", default=0)
- time_unblock = models.DateTimeField("Время разблокировки", blank=True)
- status = models.BooleanField("Статус блокировки", default=False)
- def __str__(self):
- return self.ip_address
- class TemporaryBanIpAdmin(admin.ModelAdmin):
- list_display = ('ip_address', 'status', 'attempts', 'time_unblock')
- search_fields = ('ip_address',)
admin.py
Регистрация модели в админке
- from django.contrib import admin
- from .models import TemporaryBanIp, TemporaryBanIpAdmin
- admin.site.register(TemporaryBanIp, TemporaryBanIpAdmin)
views.py
Модифицируем метод post кастомизированной страницы авторизации из прошлой статьи. В данном коде также используется специальная функция для получения IP адреса из запроса .
- class ELoginView(View):
- # код метода get
- def post(self, request):
- # забираем данные формы авторизации из запроса
- form = AuthenticationForm(request, data=request.POST)
- # забираем IP адрес из запроса
- ip = get_client_ip(request)
- # получаем или создаём новую запись об IP, с которого вводится пароль, на предмет блокировки
- obj, created = TemporaryBanIp.objects.get_or_create(
- defaults={
- 'ip_address': ip,
- 'time_unblock': timezone.now()
- },
- ip_address=ip
- )
- # если IP заблокирован и время разблокировки не настало
- if obj.status is True and obj.time_unblock > timezone.now():
- context = create_context_username_csrf(request)
- if obj.attempts == 3 or obj.attempts == 6:
- # то открываем страницу с сообщением о блокировки на 15 минут при 3 и 6 неудачных попытках входа
- return render_to_response('accounts/block_15_minutes.html', context=context)
- elif obj.attempts == 9:
- # или открываем страницу о блокировке на 24 часа, при 9 неудачных попытках входа
- return render_to_response('accounts/block_24_hours.html', context=context)
- elif obj.status is True and obj.time_unblock < timezone.now():
- # если IP заблокирован, но время разблокировки настало, то разблокируем IP
- obj.status = False
- obj.save()
- # если пользователь ввёл верные данные, то авторизуем его и удаляем запись о блокировке IP
- if form.is_valid():
- auth.login(request, form.get_user())
- obj.delete()
- next = urlparse(get_next_url(request)).path
- if next == '/admin/login/' and request.user.is_staff:
- return redirect('/admin/')
- return redirect(next)
- else:
- # иначе считаем попытки и устанавливаем время разблокировки и статус блокировки
- obj.attempts += 1
- if obj.attempts == 3 or obj.attempts == 6:
- obj.time_unblock = timezone.now() + timezone.timedelta(minutes=15)
- obj.status = True
- elif obj.attempts == 9:
- obj.time_unblock = timezone.now() + timezone.timedelta(1)
- obj.status = True
- elif obj.attempts > 9:
- obj.attempts = 1
- obj.save()
- context = create_context_username_csrf(request)
- context['login_form'] = form
- return render_to_response('accounts/login.html', context=context)
Таким вот способом можно сделать довольно простое противодействие брутфорсу пароля для небольшого сайта на Django.
Для Django рекомендую VDS-сервера хостера Timeweb .