comparison roundup/cgi/templating.py @ 5488:52cb53eedf77

reworked random number use prefer secrets module from Python 3.6+, random.SystemRandom and finally plain random
author Christof Meerwald <cmeerw@cmeerw.org>
date Sat, 04 Aug 2018 22:40:16 +0100
parents b0359a7c5b6d
children 6b0c542642be
comparison
equal deleted inserted replaced
5487:ce171c81d823 5488:52cb53eedf77
18 """ 18 """
19 19
20 __docformat__ = 'restructuredtext' 20 __docformat__ = 'restructuredtext'
21 21
22 22
23 import cgi, re, os.path, mimetypes, csv, string 23 import base64, cgi, re, os.path, mimetypes, csv, string
24 import calendar 24 import calendar
25 import textwrap 25 import textwrap
26 import time, hashlib 26 import time, hashlib
27 27
28 from roundup.anypy import urllib_ 28 from roundup.anypy import urllib_
29 from roundup import hyperdb, date, support 29 from roundup import hyperdb, date, support
30 from roundup import i18n 30 from roundup import i18n
31 from roundup.i18n import _ 31 from roundup.i18n import _
32 from roundup.anypy.strings import is_us, s2b, us2s, s2u, u2s, StringIO 32 from roundup.anypy.strings import is_us, b2s, s2b, us2s, s2u, u2s, StringIO
33 33
34 from .KeywordsExpr import render_keywords_expression_editor 34 from .KeywordsExpr import render_keywords_expression_editor
35 35
36 try: 36 import roundup.anypy.random_ as random_
37 # Use the cryptographic source of randomness if available
38 from random import SystemRandom
39 random=SystemRandom()
40 except ImportError:
41 from random import random
42 try: 37 try:
43 import cPickle as pickle 38 import cPickle as pickle
44 except ImportError: 39 except ImportError:
45 import pickle 40 import pickle
46 try: 41 try:
66 # this global translation service is not thread-safe. 61 # this global translation service is not thread-safe.
67 # it is left here for backward compatibility 62 # it is left here for backward compatibility
68 # until all Web UI translations are done via client.translator object 63 # until all Web UI translations are done via client.translator object
69 translationService = TranslationService.get_translation() 64 translationService = TranslationService.get_translation()
70 65
71 def anti_csrf_nonce(self, client, lifetime=None): 66 def anti_csrf_nonce(client, lifetime=None):
72 ''' Create a nonce for defending against CSRF attack. 67 ''' Create a nonce for defending against CSRF attack.
73
74 This creates a nonce by hex encoding the sha256 of
75 random.random(), the address of the object requesting
76 the nonce and time.time().
77 68
78 Then it stores the nonce, the session id for the user 69 Then it stores the nonce, the session id for the user
79 and the user id in the one time key database for use 70 and the user id in the one time key database for use
80 by the csrf validator that runs in the client::inner_main 71 by the csrf validator that runs in the client::inner_main
81 module/function. 72 module/function.
82 ''' 73 '''
83 otks=client.db.getOTKManager() 74 otks=client.db.getOTKManager()
84 # include id(self) as the exact location of self (including address) 75 key = b2s(base64.b32encode(random_.token_bytes(40)))
85 # is unpredicatable (depends on number of previous connections etc.)
86 key = '%s%s%s'%(random.random(),id(self),time.time())
87 key = hashlib.sha256(s2b(key)).hexdigest()
88 76
89 while otks.exists(key): 77 while otks.exists(key):
90 key = '%s%s%s'%(random.random(),id(self),time.time()) 78 key = b2s(base64.b32encode(random_.token_bytes(40)))
91 key = hashlib.sha256(s2b(key)).hexdigest()
92 79
93 # lifetime is in minutes. 80 # lifetime is in minutes.
94 if lifetime is None: 81 if lifetime is None:
95 lifetime = client.db.config['WEB_CSRF_TOKEN_LIFETIME'] 82 lifetime = client.db.config['WEB_CSRF_TOKEN_LIFETIME']
96 83
782 """ 769 """
783 if not self.is_edit_ok(): 770 if not self.is_edit_ok():
784 return '' 771 return ''
785 772
786 return self.input(type="hidden", name="@csrf", 773 return self.input(type="hidden", name="@csrf",
787 value=anti_csrf_nonce(self, self._client)) + \ 774 value=anti_csrf_nonce(self._client)) + \
788 '\n' + \ 775 '\n' + \
789 self.input(type="hidden", name="@action", value=action) + \ 776 self.input(type="hidden", name="@action", value=action) + \
790 '\n' + \ 777 '\n' + \
791 self.input(type="submit", name="submit_button", value=self._(label)) 778 self.input(type="submit", name="submit_button", value=self._(label))
792 779
925 """ 912 """
926 return self.input(type="hidden", name="@lastactivity", 913 return self.input(type="hidden", name="@lastactivity",
927 value=self.activity.local(0)) + \ 914 value=self.activity.local(0)) + \
928 '\n' + \ 915 '\n' + \
929 self.input(type="hidden", name="@csrf", 916 self.input(type="hidden", name="@csrf",
930 value=anti_csrf_nonce(self, self._client)) + \ 917 value=anti_csrf_nonce(self._client)) + \
931 '\n' + \ 918 '\n' + \
932 self.input(type="hidden", name="@action", value=action) + \ 919 self.input(type="hidden", name="@action", value=action) + \
933 '\n' + \ 920 '\n' + \
934 self.input(type="submit", name="submit_button", 921 self.input(type="submit", name="submit_button",
935 value=self._(label)) 922 value=self._(label))
3080 def Batch(self, sequence, size, start, end=0, orphan=0, overlap=0): 3067 def Batch(self, sequence, size, start, end=0, orphan=0, overlap=0):
3081 return Batch(self.client, sequence, size, start, end, orphan, 3068 return Batch(self.client, sequence, size, start, end, orphan,
3082 overlap) 3069 overlap)
3083 3070
3084 def anti_csrf_nonce(self, lifetime=None): 3071 def anti_csrf_nonce(self, lifetime=None):
3085 return anti_csrf_nonce(self, self.client, lifetime=lifetime) 3072 return anti_csrf_nonce(self.client, lifetime=lifetime)
3086 3073
3087 def url_quote(self, url): 3074 def url_quote(self, url):
3088 """URL-quote the supplied text.""" 3075 """URL-quote the supplied text."""
3089 return urllib_.quote(url) 3076 return urllib_.quote(url)
3090 3077

Roundup Issue Tracker: http://roundup-tracker.org/