Django/Forms: как я могу проверить исходные данные на соответствие полученным данным (если совпадают)?

Допустим, я создаю форму и передаю через привязку какие-то скрытые значения, которые нельзя менять. Мой вопрос: как я могу проверить, изменил ли злоумышленник эти скрытые значения? Я не уверен, что именно связывает данные в форме и чем отличается начальный.

В Django forms.py есть свойство, называемое changed_data, но я не знаю, может помочь или нет.

Код для демонстрации:

forms.py

class ConfirmForm(forms.Form):
    client_id = forms.CharField(widget=forms.HiddenInput())
    identifier = forms.CharField(widget=forms.HiddenInput())

    def clean(self):
        # Maybe here the validation process of cliend_id and identifier like:
        clean_client_id = self.cleaned_data.get('client_id')
        clean_identifier = self.cleaned_data.get('identifier')
        if last_client_id == clean_client_id and 
           last_identifier == clean_identifier:
            return self.cleaned_data
        else:
            raise forms.ValidationError("False data.")

views.py

def form_confirm_handler(request):
    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():
            #Do something...
            return redirect('home:index')

    #The following values are not fixed. Are generated variables!
    bound_data = {'client_id':'123456','identifier':'wuiy5895'}
    form = ConfirmForm(bound_data)
    return render(request, 'client/theform.html', {'form':form})

html-шаблон

<form action="{% url 'client:confirm' %}" method="post">
    <p>Do you really want to proceed?</p>
    {% csrf_token %}
    {{ form.client_id }}
    {{ form.identifier }}
    <button id="submit" type="submit" name="submit" class="btn" value="accept">Accept</button>
    <button id="cancel" type="submit" name="cancel" class="btn btn-primary" value="cancel">Cancel</button>
</form>

Заранее спасибо!


person CodeArtist    schedule 12.04.2013    source источник
comment
почему бы не создать скрытое поле для начального значения?   -  person catherine    schedule 13.04.2013
comment
извините, я не понимаю, client_id и идентификатор уже являются скрытыми полями для этой цели.   -  person CodeArtist    schedule 13.04.2013
comment
взгляните на этот QA   -  person LarsVegas    schedule 13.04.2013


Ответы (3)


Я нашел 4 (простых) возможных решения этой проблемы.

Наиболее правильное решение для Django:

class TheFormName():
    client_id = forms.CharField(show_hidden_initial=True, widget=forms.HiddenInput())
    identifier = forms.CharField(show_hidden_initial=True, widget=forms.HiddenInput())

def clean(self):
    if self.has_changed():
        raise forms.ValidationError('Fields are not valid.')

    return self.cleaned_data

Второе решение — использовать changed_data, чтобы увидеть, что изменилось:

def clean(self):
    for field_name in self.changed_data:
        # loop through the fields which have changed
        print "field {} has changed. New value {}".format(field_name, cleaned_data['field_name']

В моем случае это переводится так, но точно так же, как метод has_changed():

def clean(self):
    if len(self.changed_data) > 0:
        raise forms.ValidationError('Fields are not valid.')

    return self.cleaned_data

Другое решение, которое больше похоже на взлом:

self.cleaned_data['cliend_id'] == self.instance.cliend_id
self.cleaned_data['identifier'] == self.instance.identifier

И окончательное решение, немного более сложное, заключается в использовании сеансов внутри метода clean() (и вне поля зрения). Пример из Django Docs:

from django.contrib.sessions.backends.db import SessionStore
import datetime
s = SessionStore()
s['last_login'] = datetime.datetime(2005, 8, 20, 13, 35, 10)
s.save()
s.session_key
>>> '2b1189a188b44ad18c35e113ac6ceead'

s = SessionStore(session_key='2b1189a188b44ad18c35e113ac6ceead')
s['last_login']

Также полезен этот пост undocument">В Django 1.4, Form.has_changed() и Form.changed_data, которые недокументированы, работают должным образом? предоставлено @LarsVegas

person CodeArtist    schedule 13.04.2013

Создайте новое скрытое поле с именем temper_seal

temper_seal = forms.CharField(widget=forms.HiddenInput())

Установите начальное значение temper_seal на хэш client_id и identifier и некоторую случайную постоянную строку, известную только вашему серверу.

Когда форма вернется с пользовательскими данными, хешируйте значения client_id и identifier и константную строку, использованную ранее. Сравните это значение со значением, представленным в скрытом поле temper_seal. Если они совпадают, пользователь не изменил данные в client_id и identifier.

person Krystian Cybulski    schedule 12.04.2013
comment
Идея не в том, чтобы быть фиксированной строкой. Я не знаю предыдущее сгенерированное значение - person CodeArtist; 12.04.2013
comment
@JorgeCode Пользователь может изменить все на стороне клиента. Если сервер заранее не знает исходные данные, я не думаю, что вы можете что-то сделать, чтобы проверить достоверность. - person Cianan Sims; 13.04.2013
comment
Да, это идея. Например. Я генерирую идентификатор (скажем, число), поэтому я хочу, чтобы этот номер каким-то образом запомнился сервером, а затем, когда пользователь отправляет форму, сервер проверяет данные на соответствие исходным данным. Я не знаю, как сервер запомнит исходные данные. - person CodeArtist; 13.04.2013
comment
Вы можете сделать несколько вещей, если начальное значение генерируется сервером. Одним из очевидных является запись в базу данных. Затем сравните его с полученным значением. Другой способ — отправить клиенту хэш начального значения и некоторый секрет, известный только на вашем сервере, и сохранить его в другом скрытом поле. Когда значения вернутся, хэшируйте значение из первого скрытого поля, объединенного с секретом вашего сервера, и сравните его с хешем, хранящимся во втором скрытом поле. Если так же, ничего не изменилось. В противном случае, это было закалено. Я обновил ответ. - person Krystian Cybulski; 13.04.2013

Я думал, можете ли вы использовать переменные сеанса.

def form_confirm_handler(request):
    if request.method == 'POST':
        form = ConfirmForm(request.POST)
        if form.is_valid():

            if form.cleaned_data.get['client_id'] == request.session.get('client_id'):
                //.....
            else:
                //.....

            //delete the session after comparing
            del request.session['client_id']
            del request.session['identifier']

            return redirect('home:index')

    #The following values are not fixed. Are generated variables!

    request.session['client_id'] = '123456'
    request.session['identifier'] = 'wuiy5895'

    bound_data = {
        'client_id': request.session.get('client_id'), 
        'identifier': request.session.get('identifier')
    }

    form = ConfirmForm(bound_data)
    return render(request, 'client/theform.html', {'form':form})
person catherine    schedule 13.04.2013
comment
Спасибо, Екатерина, действительно работает над вашим примером, но я думал о более элегантном решении. Все проверки должны выполняться внутри clean(). - person CodeArtist; 13.04.2013
comment
Это первое, я думаю. Не волнуйтесь, если я придумаю другое, я опубликую его здесь. Хммммм... Я кое-что подумал, я не знаю, работает ли это, нужно проверить - person catherine; 13.04.2013
comment
ваше решение номер 2 я уже видел, но я не знаю, как его использовать. changed_data и has_changed - person catherine; 13.04.2013