devalone
@devalone
̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻̻

Django. Как защитить форму от множественной отправки?

Есть ли готовые решения для защиты форм от спама? Лучше, чтобы назойливая капча появлялась не сразу, а после подозрительных действий, допустим, много запросов с одного ip в определённый промежуток времени, большое количество ссылок в тексте или, если это форма логина, то можно считать подозрительным большое количество попыток входа в один аккаунт с разных ip адресов.
  • Вопрос задан
  • 1044 просмотра
Пригласить эксперта
Ответы на вопрос 2
sergey-gornostaev
@sergey-gornostaev Куратор тега Django
Седой и строгий
class FloodSafeMixin(object):
    def __init__(self, *args, **kwargs):
        request = kwargs.pop('request', None)
        if request:
            self._user = request.user.username if request.user.is_authenticated() else None
            self._ip = get_ip(request)
            self._period = OrderedDict()
            self._period['days'] = kwargs.pop('days', None)
            self._period['hours'] = kwargs.pop('hours', None)
            self._period['minutes'] = kwargs.pop('minutes', None)
            self._period['seconds'] = kwargs.pop('seconds', None)
            self._period['milliseconds'] = kwargs.pop('milliseconds', None)
            if not any(self._period.values()):
                self._period = {'minutes': 1}
            else:
                self._period = OrderedDict([(k, v) for k, v in self._period.items() if v])
        else:
            self._ip = None
        super(FloodSafeMixin, self).__init__(*args, **kwargs)


    def clean(self):
        cleaned_data = super(FloodSafeMixin, self).clean()
        sender_id = hashlib.md5((self._user if self._user else self._ip).encode('utf-8')).hexdigest()
        if sender_id:
            class_name = self.__class__.__name__
            cache_name = 'last-submit.{0}.{1}'.format(class_name, sender_id)

            now = datetime.now()
            last_submit = cache.get(cache_name, now - timedelta(days=1))
            if (now - last_submit) < timedelta(**self._period):
                cache.set(cache_name, now)
                period = ' '.join([u'{0} {1}'.format(v, _p('genitive', k)) for k, v in self._period.items()])
                raise forms.ValidationError(_('Form submitted less than %(period)s ago'), code='flood',
                    params={'period': period})
            else:
                if not self.errors:
                    cache.set(cache_name, now)
        return cleaned_data


class FeedbackForm(FloodSafeMixin, forms.Form):
    ...


class SomeFormHandlerView(FormView):
    def get_form_kwargs(self):
        kwargs = super(GenericFormHandlerView, self).get_form_kwargs()
        kwargs['request'] = self.request
        kwargs['minutes'] = 2
        return kwargs
    ...
Ответ написан
Комментировать
@artinnok
бекенд-программист
На мой взгляд, лучше написать кастомную мидлварь, которая будет защищать вас от тупого брутфорса. Логика работы навскидку:
  1. Чекать количество запросов в минуту при POST-запросах
  2. Проверять адрес запрос на то, является ли он прокси-сервером, к примеру, хотя бы так
  3. Позволять делать POST-запросы, только если был сделан хотя бы 1 GET-запрос


Если хотите сделать защиту на фронте - используйте, к примеру, reCAPTCHA. Спасет вас от тупых ботов.

Пихать всю логику защиты в форму - странное решение, очень некрасивое и громоздкое, плюс к тому же, нерасширяемое.

В форме - только валидация и ничего больше. Никто не будет смотреть в код формы, при защите от брутфорса.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы