From e14a7b3df86969d478090b314d9660b6d835afa7 Mon Sep 17 00:00:00 2001 From: onovy Date: Tue, 21 Apr 2015 10:15:14 +0200 Subject: [PATCH] Security fixies. Generate new salt for every password. Support different salt for passwords. Support SHA512 for hashing. --- etc/proxy-server.conf-sample | 4 +- swauth/authtypes.py | 78 ++++++++++++++++++++++++++++++++---- swauth/middleware.py | 1 - 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/etc/proxy-server.conf-sample b/etc/proxy-server.conf-sample index 3ffbe1a..fe20e98 100644 --- a/etc/proxy-server.conf-sample +++ b/etc/proxy-server.conf-sample @@ -47,11 +47,9 @@ use = egg:swauth#swauth # max_token_life = # Specifies how the user key is stored. The default is 'plaintext', leaving the # key unsecured but available for key-signing features if such are ever added. -# An alternative is 'sha1' which stores only a one-way hash of the key leaving +# An alternative is 'sha512' which stores only a one-way hash of the key leaving # it secure but unavailable for key-signing. # auth_type = plaintext -# Used if the auth_type is sha1 or another method that can make use of a salt. -# auth_type_salt = swauthsalt # This allows middleware higher in the WSGI pipeline to override auth # processing, useful for middleware such as tempurl and formpost. If you know # you're not going to use such middleware and you want a bit of extra security, diff --git a/swauth/authtypes.py b/swauth/authtypes.py index 90aad72..256ec3b 100644 --- a/swauth/authtypes.py +++ b/swauth/authtypes.py @@ -28,13 +28,10 @@ conditions: - Write a match(key, creds) method that will take two arguments: the user's key, and the user's retrieved credentials. Return a boolean value that indicates whether the match is True or False. - -Note that, since some of the encodings will be hashes, swauth supports the -notion of salts. Thus, self.salt will be set to either a user-specified salt -value or to a default value. """ import hashlib +import os #: Maximum length any valid token should ever be. @@ -80,6 +77,20 @@ class Sha1(object): must be capitalized. encode and match methods must be provided and are the only ones that will be used by swauth. """ + + def encode_w_salt(self, salt, key): + """ + Encodes a user key with salt into a particular format. The result of + this method will be used internally. + + :param salt: Salt for hashing + :param key: User's secret key + :returns: A string representing user credentials + """ + enc_key = '%s%s' % (salt, key) + enc_val = hashlib.sha1(enc_key).hexdigest() + return "sha1:%s$%s" % (salt, enc_val) + def encode(self, key): """ Encodes a user key into a particular format. The result of this method @@ -88,9 +99,8 @@ class Sha1(object): :param key: User's secret key :returns: A string representing user credentials """ - enc_key = '%s%s' % (self.salt, key) - enc_val = hashlib.sha1(enc_key).hexdigest() - return "sha1:%s$%s" % (self.salt, enc_val) + salt = os.urandom(32).encode('base64').rstrip(); + return self.encode_w_salt(salt, key) def match(self, key, creds): """ @@ -100,4 +110,56 @@ class Sha1(object): :param creds: User's stored credentials :returns: True if the supplied key is valid, False otherwise """ - return self.encode(key) == creds + + [type, rest] = creds.split(':') + [salt, enc] = rest.split('$') + + return self.encode_w_salt(salt, key) == creds + +class Sha512(object): + """ + Provides a particular auth type for encoding format for encoding and + matching user keys. + + This class must be all lowercase except for the first character, which + must be capitalized. encode and match methods must be provided and are + the only ones that will be used by swauth. + """ + + def encode_w_salt(self, salt, key): + """ + Encodes a user key with salt into a particular format. The result of + this method will be used internal. + + :param salt: Salt for hashing + :param key: User's secret key + :returns: A string representing user credentials + """ + enc_key = '%s%s' % (salt, key) + enc_val = hashlib.sha512(enc_key).hexdigest() + return "sha512:%s$%s" % (salt, enc_val) + + def encode(self, key): + """ + Encodes a user key into a particular format. The result of this method + will be used by swauth for storing user credentials. + + :param key: User's secret key + :returns: A string representing user credentials + """ + salt = os.urandom(32).encode('base64').rstrip(); + return self.encode_w_salt(salt, key) + + def match(self, key, creds): + """ + Checks whether the user-provided key matches the user's credentials + + :param key: User-supplied key + :param creds: User's stored credentials + :returns: True if the supplied key is valid, False otherwise + """ + + [type, rest] = creds.split(':') + [salt, enc] = rest.split('$') + + return self.encode_w_salt(salt, key) == creds diff --git a/swauth/middleware.py b/swauth/middleware.py index 2c9a4df..ad3000c 100644 --- a/swauth/middleware.py +++ b/swauth/middleware.py @@ -140,7 +140,6 @@ class Swauth(object): if self.auth_encoder is None: raise Exception('Invalid auth_type in config file: %s' % self.auth_type) - self.auth_encoder.salt = conf.get('auth_type_salt', 'swauthsalt') self.allow_overrides = \ conf.get('allow_overrides', 't').lower() in TRUE_VALUES self.agent = '%(orig)s Swauth'