[Checkins] SVN: zope.app.authentication/trunk/ Merge changes from ulif-saltfix into trunk.
Uli Fouquet
uli at gnufix.de
Thu Jan 22 04:18:29 EST 2009
Log message for revision 94925:
Merge changes from ulif-saltfix into trunk.
Changed:
U zope.app.authentication/trunk/CHANGES.txt
U zope.app.authentication/trunk/src/zope/app/authentication/password.py
U zope.app.authentication/trunk/src/zope/app/authentication/password.zcml
U zope.app.authentication/trunk/src/zope/app/authentication/placelesssetup.py
-=-
Modified: zope.app.authentication/trunk/CHANGES.txt
===================================================================
--- zope.app.authentication/trunk/CHANGES.txt 2009-01-22 08:26:25 UTC (rev 94924)
+++ zope.app.authentication/trunk/CHANGES.txt 2009-01-22 09:18:28 UTC (rev 94925)
@@ -4,8 +4,14 @@
3.5.0 (unreleased)
-* ...
+* Encoded passwords are now stored with a prefix ({MD5}, {SHA1},
+ {SSHA}) indicating the used encoding schema. Old (encoded) passwords
+ can still be used.
+* Add an SSHA password manager that is compatible with standard LDAP
+ passwords. As this encoding gives better security agains dictionary
+ attacks, users are encouraged to switch to this new password schema.
+
3.4.4 (2008-12-12)
------------------
Modified: zope.app.authentication/trunk/src/zope/app/authentication/password.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/password.py 2009-01-22 08:26:25 UTC (rev 94924)
+++ zope.app.authentication/trunk/src/zope/app/authentication/password.py 2009-01-22 09:18:28 UTC (rev 94925)
@@ -24,6 +24,9 @@
from md5 import new as md5
from sha import new as sha1
+from base64 import urlsafe_b64encode
+from base64 import urlsafe_b64decode
+from os import urandom
from random import randint
from codecs import getencoder
@@ -68,6 +71,9 @@
class MD5PasswordManager(PlainTextPasswordManager):
"""MD5 password manager.
+ Note: use of salt in this password manager is purely
+ cosmetical. Use SSHA if you want increased security.
+
>>> from zope.interface.verify import verifyObject
>>> manager = MD5PasswordManager()
@@ -77,7 +83,7 @@
>>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
>>> encoded = manager.encodePassword(password, salt="")
>>> encoded
- '86dddccec45db4599f1ac00018e54139'
+ '{MD5}86dddccec45db4599f1ac00018e54139'
>>> manager.checkPassword(encoded, password)
True
>>> manager.checkPassword(encoded, password + u"wrong")
@@ -100,16 +106,22 @@
def encodePassword(self, password, salt=None):
if salt is None:
salt = "%08x" % randint(0, 0xffffffff)
- return salt + md5(_encoder(password)[0]).hexdigest()
+ return '{MD5}%s%s' % (salt, md5(_encoder(password)[0]).hexdigest())
def checkPassword(self, storedPassword, password):
+ if storedPassword.startswith('{MD5}'):
+ salt = storedPassword[5:-32]
+ return storedPassword == self.encodePassword(password, salt)
salt = storedPassword[:-32]
- return storedPassword == self.encodePassword(password, salt)
+ return storedPassword == self.encodePassword(password, salt)[6:]
class SHA1PasswordManager(PlainTextPasswordManager):
"""SHA1 password manager.
+ Note: use of salt in this password manager is purely
+ cosmetical. Use SSHA if you want increased security.
+
>>> from zope.interface.verify import verifyObject
>>> manager = SHA1PasswordManager()
@@ -119,7 +131,7 @@
>>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
>>> encoded = manager.encodePassword(password, salt="")
>>> encoded
- '04b4eec7154c5f3a2ec6d2956fb80b80dc737402'
+ '{SHA1}04b4eec7154c5f3a2ec6d2956fb80b80dc737402'
>>> manager.checkPassword(encoded, password)
True
>>> manager.checkPassword(encoded, password + u"wrong")
@@ -142,18 +154,86 @@
def encodePassword(self, password, salt=None):
if salt is None:
salt = "%08x" % randint(0, 0xffffffff)
- return salt + sha1(_encoder(password)[0]).hexdigest()
+ return '{SHA1}%s%s' % (salt, sha1(_encoder(password)[0]).hexdigest())
def checkPassword(self, storedPassword, password):
+ if storedPassword.startswith('{SHA1}'):
+ salt = storedPassword[6:-40]
+ return storedPassword == self.encodePassword(password, salt)
salt = storedPassword[:-40]
+ return storedPassword == self.encodePassword(password, salt)[7:]
+
+class SSHAPasswordManager(PlainTextPasswordManager):
+ """SSHA password manager.
+
+ SSHA is basically SHA1-encoding which also incorporates a salt
+ into the encoded string. This way, stored passwords are more
+ robust against dictionary attacks of attackers that could get
+ access to lists of encoded passwords.
+
+ SSHA is regularly used in LDAP databases and we should be
+ compatible with passwords used there.
+
+ >>> from zope.interface.verify import verifyObject
+
+ >>> manager = SSHAPasswordManager()
+ >>> verifyObject(IPasswordManager, manager)
+ True
+
+ >>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
+ >>> encoded = manager.encodePassword(password, salt="")
+ >>> encoded
+ '{SSHA}BLTuxxVMXzouxtKVb7gLgNxzdAI='
+
+ >>> manager.checkPassword(encoded, password)
+ True
+ >>> manager.checkPassword(encoded, password + u"wrong")
+ False
+
+ Using the `slappasswd` utility to encode ``secret``, we get
+ ``{SSHA}J4mrr3NQHXzLVaT0h9TuEWoJOrxeQ5lv`` as seeded hash.
+
+ Our password manager generates the same value when seeded with the
+ same salt, so we can be sure, our output is compatible with
+ standard LDAP tools that also use SSHA::
+
+ >>> from base64 import urlsafe_b64decode
+ >>> salt = urlsafe_b64decode('XkOZbw==')
+ >>> encoded = manager.encodePassword('secret', salt)
+ >>> encoded
+ '{SSHA}J4mrr3NQHXzLVaT0h9TuEWoJOrxeQ5lv'
+
+ >>> encoded = manager.encodePassword(password)
+ >>> manager.checkPassword(encoded, password)
+ True
+ >>> manager.checkPassword(encoded, password + u"wrong")
+ False
+
+ >>> manager.encodePassword(password) != manager.encodePassword(password)
+ True
+ """
+
+ implements(IPasswordManager)
+
+ def encodePassword(self, password, salt=None):
+ if salt is None:
+ salt = urandom(4)
+ hash = sha1(_encoder(password)[0])
+ hash.update(salt)
+ return '{SSHA}' + urlsafe_b64encode(
+ hash.digest() + salt)
+
+ def checkPassword(self, storedPassword, password):
+ byte_string = urlsafe_b64decode(storedPassword[6:])
+ salt = byte_string[20:]
return storedPassword == self.encodePassword(password, salt)
-
# Simple registry used by mkzopeinstance script
managers = [
("Plain Text", PlainTextPasswordManager()), # default
("MD5", MD5PasswordManager()),
("SHA1", SHA1PasswordManager()),
+ ("SSHA", SSHAPasswordManager()),
]
Modified: zope.app.authentication/trunk/src/zope/app/authentication/password.zcml
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/password.zcml 2009-01-22 08:26:25 UTC (rev 94924)
+++ zope.app.authentication/trunk/src/zope/app/authentication/password.zcml 2009-01-22 09:18:28 UTC (rev 94925)
@@ -38,4 +38,10 @@
factory=".password.SHA1PasswordManager"
/>
+ <utility
+ name="SSHA"
+ provides=".interfaces.IPasswordManager"
+ factory=".password.SSHAPasswordManager"
+ />
+
</configure>
Modified: zope.app.authentication/trunk/src/zope/app/authentication/placelesssetup.py
===================================================================
--- zope.app.authentication/trunk/src/zope/app/authentication/placelesssetup.py 2009-01-22 08:26:25 UTC (rev 94924)
+++ zope.app.authentication/trunk/src/zope/app/authentication/placelesssetup.py 2009-01-22 09:18:28 UTC (rev 94925)
@@ -22,6 +22,7 @@
from zope.app.authentication.password import PlainTextPasswordManager
from zope.app.authentication.password import MD5PasswordManager
from zope.app.authentication.password import SHA1PasswordManager
+from zope.app.authentication.password import SSHAPasswordManager
class PlacelessSetup(object):
@@ -31,3 +32,4 @@
"Plain Text")
ztapi.provideUtility(IPasswordManager, MD5PasswordManager(), "MD5")
ztapi.provideUtility(IPasswordManager, SHA1PasswordManager(), "SHA1")
+ ztapi.provideUtility(IPasswordManager, SSHAPasswordManager(), "SSHA")
More information about the Checkins
mailing list