fabio
fabio

Reputation: 1365

Check django form validity and javascript

I'm using a django model form and I want to protect me from malicious user input (not just wrong input).

From my understanding django forms are enough secure: .is_valid() check user input, csfr protect from cross site forgery.

But this time my form isn't using action='/path/to/my/view' to call a django view, instead my submit button calls a javascript function and this function takes the data and calls a django view using ajax to access the database and then shows the results on screen.

So I don't think to be protected anymore (.is_valid() is not called, csfr is not sent). I'm right? and if so what I should do?

I think:

1) This is not a problem, the form is reasonable secure (why?)

2) Refactor the code and using a django view

3) Neither the django form validation is enough secure so anyway I should do something more (what?)

4) My javascript function is sending the data to a django view using ajax. I should use that data to instantializate a bound form and use .is_valid() on that, but anyway I'm not using csfr, right?

5) Using html validators (to me they don't look adapt to check against malicious input data)

6) Other?

Some code, to be complete but probabily you wont need it

my forms.py:

class NameListForm(forms.ModelForm):
    class Meta:
        model = Name
        fields = ['namelanguage', 'nametype', 'gender']
        widgets = {
            'gender': forms.CheckboxSelectMultiple(),
        }

My models.py:

class Name(models.Model):
    name = models.CharField(_('nome'), max_length=50, default='')
    namelanguage = models.ForeignKey(
        NameLanguage, related_name='%(app_label)s_%(class)s_language',
        verbose_name=_('linguaggio'), on_delete=models.PROTECT)
    nametype = models.ForeignKey(
        NameType, related_name='%(app_label)s_%(class)s_tipo',
        verbose_name=_('tipo'), on_delete=models.PROTECT)
    gender = models.ForeignKey(
        Gender, related_name='%(app_label)s_%(class)s_gender',
        verbose_name=_('sesso'), on_delete=models.PROTECT,
        blank=True, null=True)

my template.html:

<form action="" method="post">
    <div>
        <div class="col-md-auto">
          {{ name_list_form.namelanguage.label_tag }}<br />
          {{ name_list_form.namelanguage }}
          {{ name_list_form.namelanguage.errors }}
        </div>
        <div class="col-md-auto">
          {{ name_list_form.nametype.label_tag }}<br />
          {{ name_list_form.nametype }}
          {{ name_list_form.nametype.errors }}
        </div>
        <div class="col-md-auto">
          {{ name_list_form.gender.label_tag }}<br />
          {{ name_list_form.gender }}<br />
          {{ name_list_form.gender.errors }}
        </div>
    </div>
    {{ name_list_form.non_field_errors }}
    <div>
        <button class="btn btn-primary" id='list_name' type="button" onclick="FilterBy()">{% trans "List Names" %}</button>
        <button class="btn btn-primary" type="button" onclick="RandomNames()">{% trans "Random Names" %}</button>
    </div>
    {% csrf_token %}
</form>

My javascript.js:

function FilterBy() {
    var language_id = document.getElementById("id_namelanguage").value;
    var nametype_id = document.getElementById("id_nametype").value;
    ...

    $.ajax({type: 'POST',
        url: '/lists/get-list-name/',
        data: {
            language: language_id,
            nametype: nametype_id,
            ...
        },
        success: function (lista) {
            if (lista.result === 'OK') {
            //do something
            };
        }
     });
};

My views.py:

def GetListName(request):
    if request.is_ajax():
        language = request.POST.get('language')
        nametype = request.POST.get('nametype')
        # it makes sense to check validity here? and anyway I'm not using csfr, right?
        # name_list_form = NameListForm({'namelanguage': language, 'nametype': nametype, etc})
        # if name_list_form.is_valid():
        ...
        return JsonResponse({'result': 'OK', 'data': my_dict})

Upvotes: 0

Views: 1253

Answers (1)

Nicolas Appriou
Nicolas Appriou

Reputation: 2331

CSRF and data validity are two different subject.

First, you can check for CSRF errors by sending the CSRF token in a request header check this.

Second, you can send your data the same way in JS than in a classic form.

// JS, don't forget to add your CSRF headers
$.ajax({
  method: "post",
  url: "...",
  data: {
    namelanguage: "foo",
    nametype: "bar",
    gender: "baz",
  });

And just process your form as you do. You can throw an exception if you wan't to be sure you form was submitted from the JS script, but this won't assure you that it really does. Anybody can modify the client headers to make you think so.

# python
from django.http.response import HttpResponseBadRequest, HttpResponseNotAllowed

def GetListName(request):
    if not request.is_ajax():
        return HttpResponseNotAllowed()
    form = NameListForm(data=request.POST)
    if not form.is_valid():
        return HttpResponseBadRequest()

    # do stuff with your form
    return JsonResponse({'result': 'OK', 'data': 'some data'})

Upvotes: 1

Related Questions