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': 'test@example.com'})
>>> p.is_valid()
False
>>> p.errors
{'__all__': [u'Form has been tampered!']}
>>> t = TF(initial={'age': 20, 'email': 'test@example.com'})
>>> p = TF({'name':'theju','age': 20, 'form_hash': t.initial['form_hash'],
... 'email': 'test@example.com'})
>>> p.is_valid()
True
>>> p = TF({'name':'theju','age': 20, 'form_hash': 'nonsense',
... 'email': 'test@example.com'})
>>> 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": "test@example.com"},
... exclude_fields=["email"])
>>> t.initial['form_hash']
'98a7d8b771644c7e86717c2e03d8f0a9'
>>> p = TF({"name": "theju", "age": 30, "email": "test@example.com",
... "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": "test1@example.com",
... "form_hash": t.initial["form_hash"]}, exclude_fields=["email"])
>>> p.is_valid()
True
>>> p.errors
{}
>>> p = TF({"name": "theju", "age": 20, "email": "test1@example.com",
... "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()
|
Comments