From 0972d8060262d661b80751551d19e6717e15a166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Perrin?= Date: Mon, 18 Mar 2019 04:41:21 +0100 Subject: [PATCH 1/5] patches/Restricted: allow random.SystemRandom --- product/ERP5Type/patches/Restricted.py | 3 +++ product/ERP5Type/tests/testRestrictedPythonSecurity.py | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/product/ERP5Type/patches/Restricted.py b/product/ERP5Type/patches/Restricted.py index 2350f61a844..e73046385ed 100644 --- a/product/ERP5Type/patches/Restricted.py +++ b/product/ERP5Type/patches/Restricted.py @@ -344,3 +344,6 @@ for member_id in dir(decimal): if isinstance(member, type) and issubclass(member, decimal.DecimalException): ContainerAssertions[member] = 1 del member_id, member + +from random import SystemRandom +allow_type(SystemRandom) diff --git a/product/ERP5Type/tests/testRestrictedPythonSecurity.py b/product/ERP5Type/tests/testRestrictedPythonSecurity.py index ff29c15958f..80170c5049f 100644 --- a/product/ERP5Type/tests/testRestrictedPythonSecurity.py +++ b/product/ERP5Type/tests/testRestrictedPythonSecurity.py @@ -25,8 +25,6 @@ # ############################################################################## -import unittest - from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase from Products.ERP5Type.tests.utils import createZODBPythonScript from Products.ERP5Type.tests.utils import removeZODBPythonScript @@ -123,3 +121,7 @@ class TestRestrictedPythonSecurity(ERP5TypeTestCase): 'return urlparse.parse_qsl("q=s")', expected=[('q', 's')] ) + + def testSystemRandom(self): + self.createAndRunScript('import random', + 'return random.SystemRandom().getrandbits(10)') -- 2.30.9 From f4777c6350f348b99265a816a2e7c017e4f1ae54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Perrin?= Date: Mon, 18 Mar 2019 04:41:21 +0100 Subject: [PATCH 2/5] patches/Restricted: allow os.urandom --- product/ERP5Type/patches/Restricted.py | 1 + .../ERP5Type/tests/testRestrictedPythonSecurity.py | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/product/ERP5Type/patches/Restricted.py b/product/ERP5Type/patches/Restricted.py index e73046385ed..0af676a3c5a 100644 --- a/product/ERP5Type/patches/Restricted.py +++ b/product/ERP5Type/patches/Restricted.py @@ -347,3 +347,4 @@ del member_id, member from random import SystemRandom allow_type(SystemRandom) +ModuleSecurityInfo('os').declarePublic('urandom') diff --git a/product/ERP5Type/tests/testRestrictedPythonSecurity.py b/product/ERP5Type/tests/testRestrictedPythonSecurity.py index 80170c5049f..227efde8d3f 100644 --- a/product/ERP5Type/tests/testRestrictedPythonSecurity.py +++ b/product/ERP5Type/tests/testRestrictedPythonSecurity.py @@ -125,3 +125,15 @@ class TestRestrictedPythonSecurity(ERP5TypeTestCase): def testSystemRandom(self): self.createAndRunScript('import random', 'return random.SystemRandom().getrandbits(10)') + + def test_os_urandom(self): + self.createAndRunScript('import os', + 'return os.urandom(10)') + # other "unsafe" os members are not exposed + self.assertRaises(Unauthorized, + self.createAndRunScript, 'import os', + 'return os.path.exists("/")') + self.assertRaises(Unauthorized, + self.createAndRunScript, 'import os', + 'return os.system') + -- 2.30.9 From 758fc366c954927d8250d6ed03f096754e0e8ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Perrin?= Date: Thu, 16 Jan 2020 06:24:39 +0100 Subject: [PATCH 3/5] base: generate a stronger passwords in Person_generatePassword - use system random - generate longer password with a larger space API change in an incompatible way, it's no longer possible to control the number of alpha and numeric. This was reducing a lot the number of combinations, so it's better to break so that callers stop generating too weak passwords. --- .../erp5_base/Person_generatePassword.py | 51 +------------------ .../erp5_base/Person_generatePassword.xml | 4 +- 2 files changed, 3 insertions(+), 52 deletions(-) diff --git a/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.py b/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.py index 39b6647808a..bcaa50e2088 100644 --- a/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.py +++ b/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.py @@ -1,50 +1 @@ -""" - This script generates a human readable random - password in the form 'word'+digits+'word'. - - from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/410076 - - parameters: number of 'characters' , number of 'digits' - Pradeep Kishore Gowda - License : GPL - Date : 2005.April.15 - Revision 1.2 - ChangeLog: - 1.1 - fixed typos - 1.2 - renamed functions _apart & _npart to a_part & n_part as zope does not allow functions to - start with _ -""" - -import string, random -vowels = ['a','e','i','o','u'] -consonants = [a for a in string.ascii_lowercase if a not in vowels] -digits = string.digits - -def a_part(slen): - ret = '' - for i in range(slen): - if i%2 ==0: - randid = random.randint(0,20) #number of consonants - ret += consonants[randid] - else: - randid = random.randint(0,4) #number of vowels - ret += vowels[randid] - return ret - -def n_part(slen): - ret = '' - for i in range(slen): - randid = random.randint(0,9) #number of digits - ret += digits[randid] - return ret - -fpl = alpha/2 -if alpha % 2 : - fpl = int(alpha/2) + 1 -lpl = alpha - fpl - -start = a_part(fpl) -mid = n_part(numeric) -end = a_part(lpl) -newpass = "%s%s%s" % (start,mid,end) -return newpass +return ''.join(random.SystemRandom().sample(string.letters + string.digits, length)) diff --git a/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.xml b/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.xml index ee98b10c6da..3ad714f2d5f 100644 --- a/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.xml +++ b/bt5/erp5_base/SkinTemplateItem/portal_skins/erp5_base/Person_generatePassword.xml @@ -50,7 +50,7 @@ _params - alpha=6, numeric=2 + length=20 id @@ -58,7 +58,7 @@ title - Generate human readable random password + -- 2.30.9 From 201a596c96ca2274f08de906eda7ffd1a21ff4a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Perrin?= Date: Thu, 16 Jan 2020 06:27:24 +0100 Subject: [PATCH 4/5] access_token: use os.urandom to generate token Using same method as python 3.6's secrets module and a bit longer token that what python currently recommends, since we were using very very long tokens until now (so that it does not look like a "regression") --- .../erp5_access_token/RestrictedAccessToken_init.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bt5/erp5_access_token/SkinTemplateItem/portal_skins/erp5_access_token/RestrictedAccessToken_init.py b/bt5/erp5_access_token/SkinTemplateItem/portal_skins/erp5_access_token/RestrictedAccessToken_init.py index 0455e0e6049..db2fed9f318 100644 --- a/bt5/erp5_access_token/SkinTemplateItem/portal_skins/erp5_access_token/RestrictedAccessToken_init.py +++ b/bt5/erp5_access_token/SkinTemplateItem/portal_skins/erp5_access_token/RestrictedAccessToken_init.py @@ -1,7 +1,8 @@ -alpha = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' -random_id = '' -for _ in range(0, 128): - random_id += random.choice(alpha) +import os +import base64 + +# This is python 3.6 secret.token_urlsafe +random_id = base64.urlsafe_b64encode(os.urandom(48)).rstrip(b'=').decode('ascii') # Define Reference from ID provided by portal_ids portal = context.getPortalObject() -- 2.30.9 From f25ef810148a2e8de211ae31e0d4049cb96627d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Perrin?= Date: Fri, 24 Jan 2020 06:48:10 +0100 Subject: [PATCH 5/5] credential: follow Person_generatePassword API change This script no longer allow to control the number of letters and digit --- .../erp5_credential/CredentialRequest_createUser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bt5/erp5_credential/SkinTemplateItem/portal_skins/erp5_credential/CredentialRequest_createUser.py b/bt5/erp5_credential/SkinTemplateItem/portal_skins/erp5_credential/CredentialRequest_createUser.py index adc98c2d042..ced491fa6cf 100644 --- a/bt5/erp5_credential/SkinTemplateItem/portal_skins/erp5_credential/CredentialRequest_createUser.py +++ b/bt5/erp5_credential/SkinTemplateItem/portal_skins/erp5_credential/CredentialRequest_createUser.py @@ -52,7 +52,7 @@ if not login.hasPassword(): credential_recovery.submit() else: # system should generate a password - password = context.Person_generatePassword(alpha=5, numeric=3) + password = context.Person_generatePassword() login.setPassword(password) # create a global account -- 2.30.9