Sometimes, we need to pass hidden fields with an initial value in forms but cannot trust the returned values because it could have been tampered.
So here is a form that adds an additional 'hidden' field (called 'form_hash') that hashes all the initial value of hidden fields and checks for tampering.
Pretty straightforward to use.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | from django import forms
from django.conf import settings
try:
from hashlib import md5
except ImportError:
from md5 import md5
class SecureForm(forms.Form):
"""
>>> from django import forms
>>> class TF(SecureForm):
... name = forms.CharField(max_length=10)
... age = forms.IntegerField(widget=forms.HiddenInput())
... email = forms.EmailField(widget=forms.HiddenInput(), required=False)
...
>>>
>>> t = TF(initial={'age':20})
>>> p = TF({'name':'theju','age': 30, 'form_hash': t.initial['form_hash']})
>>> p.is_valid()
False
>>> p.errors
{'__all__': [u'Form has been tampered!']}
>>> p = TF({'name':'theju','age': 20, 'form_hash': t.initial['form_hash'],
... 'email': '[email protected]'})
>>> p.is_valid()
False
>>> p.errors
{'__all__': [u'Form has been tampered!']}
>>> t = TF(initial={'age': 20, 'email': '[email protected]'})
>>> p = TF({'name':'theju','age': 20, 'form_hash': t.initial['form_hash'],
... 'email': '[email protected]'})
>>> p.is_valid()
True
>>> p = TF({'name':'theju','age': 20, 'form_hash': 'nonsense',
... 'email': '[email protected]'})
>>> p.is_valid()
False
>>> p.errors
{'__all__': [u'Form has been tampered!']}
>>>
>>> class TestForm(SecureForm):
... name = forms.CharField(max_length=10)
... age = forms.IntegerField()
...
>>> t = TestForm({'name':'theju','age':50})
>>> t.is_valid()
True
>>> t.errors
{}
>>> t = TF(initial={"name": "theju", "age": 20, "email": "[email protected]"},
... exclude_fields=["email"])
>>> t.initial['form_hash']
'98a7d8b771644c7e86717c2e03d8f0a9'
>>> p = TF({"name": "theju", "age": 30, "email": "[email protected]",
... "form_hash": t.initial["form_hash"]}, exclude_fields=["email"])
>>> p.is_valid()
False
>>> p.errors
{'__all__': [u'Form has been tampered!']}
>>> p = TF({"name": "theju", "age": 20, "email": "[email protected]",
... "form_hash": t.initial["form_hash"]}, exclude_fields=["email"])
>>> p.is_valid()
True
>>> p.errors
{}
>>> p = TF({"name": "theju", "age": 20, "email": "[email protected]",
... "form_hash": t.initial["form_hash"]}, exclude_fields=("email",))
>>> p.is_valid()
True
>>> p.errors
{}
"""
def __init__(self, *args, **kwargs):
exclude_fields = kwargs.pop("exclude_fields", None)
super(SecureForm, self).__init__(*args, **kwargs)
form_hash = forms.CharField(widget=forms.HiddenInput(), required=False)
self.fields.update({'form_hash': form_hash})
self.exclude_fields = ["form_hash"]
if exclude_fields and isinstance(exclude_fields, (list, tuple)):
self.exclude_fields += list(exclude_fields)
hash_str = u""
for name, field in self.fields.items():
if field.widget.is_hidden and name not in self.exclude_fields and (self.initial.get(name, None) or field.initial):
hash_str += unicode(self.initial.get(name, None) or field.initial)
if hash_str:
hash_val = md5(hash_str.encode("utf-8") + settings.SECRET_KEY).hexdigest()
self.initial['form_hash'] = hash_val
def clean(self):
cleaned_data = super(SecureForm, self).clean()
hash_str = u""
for name, field in self.fields.items():
if field.widget.is_hidden and name not in self.exclude_fields and cleaned_data.get(name, None):
hash_str += unicode(cleaned_data[name])
if hash_str:
hash_val = md5(hash_str.encode("utf-8") + settings.SECRET_KEY).hexdigest()
if hash_val and hash_val != cleaned_data['form_hash']:
raise forms.ValidationError("Form has been tampered!")
return cleaned_data
if __name__ == '__main__':
import doctest
doctest.testmod()
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 11 months, 1 week ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 2 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Please login first before commenting.