Using Django Model Primary Key in Custom Forms THE RIGHT WAY

OWASP Django form
Image: OWASP Django form (License: CC-BY-SA Marcelo Canina)

Use PK in Django Custom forms safely

Published: Tag Django

Overview

If for some reason you aren’t using a ModelForm directly but you need to create a Django form and include a model instance primary key in it to later processing it at a POST request, then you need to make sure it can’t be edited.

Short answer: Use forms.fields.Field.disabled.

Using the PK in a form

Suppose you have a form where you get a model instance passed as a parameter (sentence_main) and in its init initalize a new form field:

from django import forms

class SentenceForm(forms.Form):
    """Used in full *correction*, and in single sentence *edit* """
    def __init__(self, *args, **kwargs):
        """Generate a field for each text's sentence"""
        sentence_main = kwargs.pop('sentence_main')
        super().__init__(*args, **kwargs)
        
        self.fields["sentencemain"] = forms.CharField(
            required=False,
            label=sentence_main.display(),
            initial=sentence_main.display(), #"initiallll", #unit.literal_translation(text_l1),
        )

Hidden PK field form

To also have the primary key of this model instance in the form, we include it using a hidden field, with the forms.HiddenInput widget.

A HiddenInput field is a special Input class with input_type = 'hidden'.

input elements of type "hidden" let web developers include data that cannot be seen or modified by users when a form is submitted. For example, the ID of the content that is currently being ordered or edited, or a unique security token. Hidden inputs are completely invisible in the rendered page, and there is no way to make it visible in the page's content.

Then in our forms.py:

class SentenceForm(forms.Form):
    """Used in full *correction*, and in single sentence *edit* """
    def __init__(self, *args, **kwargs):
	    ...
        #build the fields
        self.fields["sentencemain_pk"] = forms.IntegerField(
	        ...
            widget=forms.HiddenInput()
        )

Avoid PK edit in POST

Finally to avoid Web Parameter Tampering, we make sure the primary key can’t be modified in a POST request.

This is a very important step often omitted in tutorials to include a primary key in a form, allowing malicious users to alter the primary key processed in a POST request.

The Web Parameter Tampering attack is based on the manipulation of parameters exchanged between client and server in order to modify application data, such as user credentials and permissions, price and quantity of products, etc. Usually, this information is stored in cookies, hidden form fields, or URL Query Strings, and is used to increase application functionality and control.

This attack can be performed by a malicious user who wants to exploit the application for their own benefit, or an attacker who wishes to attack a third-person using a Man-in-the-middle attack.

To avoid the user altering this field, we use Django’s disabled argument.

class SentenceForm(forms.Form):
    """Used in full *correction*, and in single sentence *edit* """
    def __init__(self, *args, **kwargs):
	    ...
        #build the fields
        self.fields["sentencemain_pk"] = forms.IntegerField(
	        ...
            disabled = True,  #it won’t be editable by users
            widget=forms.HiddenInput()
        )

And we can trust in the primary key passed in the form when processing the POST request in a view.

The disabled boolean argument, when set to True, disables a form field using the disabled HTML attribute so that it won’t be editable by users. Even if a user tampers with the field’s value submitted to the server, it will be ignored in favor of the value from the form’s initial data.

References

Uruguay
Marcelo Canina
I'm Marcelo Canina, a developer from Uruguay. I build websites and web-based applications from the ground up and share what I learn here.
comments powered by Disqus


How to use a Primary Key from a model instance in a Django Form without the possibility of being altered in a POST request

Clutter-free software concepts.
Translations English Español

Except as otherwise noted, the content of this page is licensed under CC BY-NC-ND 4.0 . Terms and Policy.

Powered by SimpleIT Hugo Theme

·