[IMP] web: upgrade the cache to use SHA2 over SHA1
SHA-1 is a cryptographic hash function that have weaknesses known since 2005, it has been deprecated by the NIST [1] about 10 years ago in 2011 and Google [2] have been able to perform a collision attack in 2017. We use SHA-1 in order to generate unique URL for resources that can be cached by the browser: assets bundle, translations, qweb templates and qweb images. Although practical attacks still requires quite a lot of computational resources, it is time to upgrade SHA-1 to SHA-2. We have selected the SHA-512/256 variant of the SHA-2 algorithm as replacement for SHA-1 for the following reasons: * On 64 bits platform, SHA-512 is the fastest SHA-2 variant, it is only ~1.5x slower than SHA-1. [3] * Keeping only the 256 foremost bits protects against both collision attacks and length extension attacks. * The hexadecimal digest is only 24 chars longer than SHA-1 which is nice to have somewhat short URLs. We have not used SHA-3 because: * At the moment of writing, it is too slow (~3x slower than SHA-1) [3] * It is not guaranteed to be available with the Python 3.5 `hashlib` module. * One of the author of SHA-3 is Belgian. [1] https://csrc.nist.gov/projects/hash-functions/nist-policy-on-hash-functions [2] https://shattered.io/ [3] http://bench.cr.yp.to/results-hash.html [4] http://www.commitstrip.com/en/2017/02/27/the-sha-1-alternative/
This commit is contained in:
@@ -265,7 +265,7 @@ class IrHttp(models.AbstractModel):
|
||||
session_info.update({
|
||||
'translationURL': '/website/translations/',
|
||||
'cache_hashes': {
|
||||
'translations': hashlib.sha1(json.dumps(translation_cache, sort_keys=True).encode()).hexdigest(),
|
||||
'translations': hashlib.sha512(json.dumps(translation_cache, sort_keys=True).encode()).hexdigest()[:64], # sha512/256
|
||||
},
|
||||
})
|
||||
return session_info
|
||||
|
||||
@@ -567,7 +567,7 @@ class HomeStaticTemplateHelpers(object):
|
||||
:returns: (concatenation_result, checksum)
|
||||
:rtype: (bytes, str)
|
||||
"""
|
||||
checksum = hashlib.new('sha1')
|
||||
checksum = hashlib.new('sha512') # sha512/256
|
||||
if not file_dict:
|
||||
return b'', checksum.hexdigest()
|
||||
|
||||
@@ -586,7 +586,7 @@ class HomeStaticTemplateHelpers(object):
|
||||
for template in addon.values():
|
||||
root.append(template)
|
||||
|
||||
return etree.tostring(root, encoding='utf-8') if root is not None else b'', checksum.hexdigest()
|
||||
return etree.tostring(root, encoding='utf-8') if root is not None else b'', checksum.hexdigest()[:64]
|
||||
|
||||
def _get_qweb_templates(self):
|
||||
"""One and only entry point that gets and evaluates static qweb templates
|
||||
|
||||
@@ -62,9 +62,9 @@ class Http(models.AbstractModel):
|
||||
"show_effect": True,
|
||||
"display_switch_company_menu": user.has_group('base.group_multi_company') and len(user.company_ids) > 1,
|
||||
"cache_hashes": {
|
||||
"load_menus": hashlib.sha1(menu_json_utf8).hexdigest(),
|
||||
"load_menus": hashlib.sha512(menu_json_utf8).hexdigest()[:64], # sha512/256
|
||||
"qweb": qweb_checksum,
|
||||
"translations": hashlib.sha1(translations_json_utf8).hexdigest(),
|
||||
"translations": hashlib.sha512(translations_json_utf8).hexdigest()[:64], # sha512/256
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class Image(models.AbstractModel):
|
||||
if max_width or max_height:
|
||||
max_size = '%sx%s' % (max_width, max_height)
|
||||
|
||||
sha = hashlib.sha1(str(getattr(record, '__last_update')).encode('utf-8')).hexdigest()[0:7]
|
||||
sha = hashlib.sha512(str(getattr(record, '__last_update')).encode('utf-8')).hexdigest()[:7]
|
||||
max_size = '' if max_size is None else '/%s' % max_size
|
||||
|
||||
if options.get('filename-field') and getattr(record, options['filename-field'], None):
|
||||
|
||||
@@ -787,7 +787,7 @@ class Website(models.Model):
|
||||
def image_url(self, record, field, size=None):
|
||||
""" Returns a local url that points to the image field of a given browse record. """
|
||||
sudo_record = record.sudo()
|
||||
sha = hashlib.sha1(str(getattr(sudo_record, '__last_update')).encode('utf-8')).hexdigest()[0:7]
|
||||
sha = hashlib.sha512(str(getattr(sudo_record, '__last_update')).encode('utf-8')).hexdigest()[:7]
|
||||
size = '' if size is None else '/%s' % size
|
||||
return '/web/image/%s/%s/%s%s?unique=%s' % (record._name, record.id, field, size, sha)
|
||||
|
||||
|
||||
@@ -186,10 +186,10 @@ class AssetsBundle(object):
|
||||
def checksum(self):
|
||||
"""
|
||||
Not really a full checksum.
|
||||
We compute a SHA1 on the rendered bundle + max linked files last_modified date
|
||||
We compute a SHA512/256 on the rendered bundle + max linked files last_modified date
|
||||
"""
|
||||
check = u"%s%s" % (json.dumps(self.files, sort_keys=True), self.last_modified)
|
||||
return hashlib.sha1(check.encode('utf-8')).hexdigest()
|
||||
return hashlib.sha512(check.encode('utf-8')).hexdigest()[:64]
|
||||
|
||||
def _get_asset_template_url(self):
|
||||
return "/web/content/{id}-{unique}/{extra}{name}{sep}{type}"
|
||||
|
||||
@@ -152,7 +152,7 @@ class IrHttp(models.AbstractModel):
|
||||
wdate = attach[0]['__last_update']
|
||||
datas = attach[0]['datas'] or b''
|
||||
name = attach[0]['name']
|
||||
checksum = attach[0]['checksum'] or hashlib.sha1(datas).hexdigest()
|
||||
checksum = attach[0]['checksum'] or hashlib.sha512(datas).hexdigest()[:64] # sha512/256
|
||||
|
||||
if (not datas and name != request.httprequest.path and
|
||||
name.startswith(('http://', 'https://', '/'))):
|
||||
|
||||
Reference in New Issue
Block a user