Hash token before storing it in Swift
Swauth uses token value as object name. Object names are logged in proxy and object servers. Anybody with access to proxy/object server logs can see token values. Attacker can use this token to access user's data in Swift store. Instead of token, hashed token (with HASH_PATH_PREFIX and HASH_PATH_SUFFIX) is used as object name now. WARNING: In deployments without memcached this patch logs out all users because tokens became invalid. CVE-2017-16613 SecurityImpact Closes-Bug: #1655781 Change-Id: I0d01e8e95400c82ef25f98e2d269532e83233c2c
This commit is contained in:
parent
54ac16ad67
commit
70af798626
@ -15,6 +15,7 @@
|
||||
|
||||
import base64
|
||||
from hashlib import sha1
|
||||
from hashlib import sha512
|
||||
import hmac
|
||||
from httplib import HTTPConnection
|
||||
from httplib import HTTPSConnection
|
||||
@ -50,6 +51,8 @@ from swift.common.middleware.acl import referrer_allowed
|
||||
from swift.common.utils import cache_from_env
|
||||
from swift.common.utils import get_logger
|
||||
from swift.common.utils import get_remote_client
|
||||
from swift.common.utils import HASH_PATH_PREFIX
|
||||
from swift.common.utils import HASH_PATH_SUFFIX
|
||||
from swift.common.utils import split_path
|
||||
from swift.common.utils import TRUE_VALUES
|
||||
from swift.common.utils import urlparse
|
||||
@ -289,6 +292,15 @@ class Swauth(object):
|
||||
env['swift.clean_acl'] = clean_acl
|
||||
return self.app(env, start_response)
|
||||
|
||||
def _get_concealed_token(self, token):
|
||||
"""Returns hashed token to be used as object name in Swift.
|
||||
|
||||
Tokens are stored in auth account but object names are visible in Swift
|
||||
logs. Object names are hashed from token.
|
||||
"""
|
||||
enc_key = "%s:%s:%s" % (HASH_PATH_PREFIX, token, HASH_PATH_SUFFIX)
|
||||
return sha512(enc_key).hexdigest()
|
||||
|
||||
def get_groups(self, env, token):
|
||||
"""Get groups for the given token.
|
||||
|
||||
@ -397,8 +409,9 @@ class Swauth(object):
|
||||
memcache_key, (time() + expires_from_now, groups),
|
||||
time=expires_from_now)
|
||||
else:
|
||||
object_name = self._get_concealed_token(token)
|
||||
path = quote('/v1/%s/.token_%s/%s' %
|
||||
(self.auth_account, token[-1], token))
|
||||
(self.auth_account, object_name[-1], object_name))
|
||||
resp = self.make_pre_authed_request(
|
||||
env, 'GET', path).get_response(self.app)
|
||||
if resp.status_int // 100 != 2:
|
||||
@ -1168,8 +1181,9 @@ class Swauth(object):
|
||||
(path, resp.status))
|
||||
candidate_token = resp.headers.get('x-object-meta-auth-token')
|
||||
if candidate_token:
|
||||
object_name = self._get_concealed_token(candidate_token)
|
||||
path = quote('/v1/%s/.token_%s/%s' %
|
||||
(self.auth_account, candidate_token[-1], candidate_token))
|
||||
(self.auth_account, object_name[-1], object_name))
|
||||
resp = self.make_pre_authed_request(
|
||||
req.environ, 'DELETE', path).get_response(self.app)
|
||||
if resp.status_int // 100 != 2 and resp.status_int != 404:
|
||||
@ -1318,8 +1332,9 @@ class Swauth(object):
|
||||
expires = None
|
||||
candidate_token = resp.headers.get('x-object-meta-auth-token')
|
||||
if candidate_token:
|
||||
object_name = self._get_concealed_token(candidate_token)
|
||||
path = quote('/v1/%s/.token_%s/%s' %
|
||||
(self.auth_account, candidate_token[-1], candidate_token))
|
||||
(self.auth_account, object_name[-1], object_name))
|
||||
delete_token = False
|
||||
try:
|
||||
if req.headers.get('x-auth-new-token', 'false').lower() in \
|
||||
@ -1362,8 +1377,9 @@ class Swauth(object):
|
||||
# Generate new token
|
||||
token = '%stk%s' % (self.reseller_prefix, uuid4().hex)
|
||||
# Save token info
|
||||
object_name = self._get_concealed_token(token)
|
||||
path = quote('/v1/%s/.token_%s/%s' %
|
||||
(self.auth_account, token[-1], token))
|
||||
(self.auth_account, object_name[-1], object_name))
|
||||
try:
|
||||
token_life = min(
|
||||
int(req.headers.get('x-auth-token-lifetime',
|
||||
@ -1439,8 +1455,9 @@ class Swauth(object):
|
||||
if expires < time():
|
||||
groups = None
|
||||
if not groups:
|
||||
object_name = self._get_concealed_token(token)
|
||||
path = quote('/v1/%s/.token_%s/%s' %
|
||||
(self.auth_account, token[-1], token))
|
||||
(self.auth_account, object_name[-1], object_name))
|
||||
resp = self.make_pre_authed_request(
|
||||
req.environ, 'GET', path).get_response(self.app)
|
||||
if resp.status_int // 100 != 2:
|
||||
|
@ -202,5 +202,6 @@ class TestSha512(unittest.TestCase):
|
||||
match = self.auth_encoder.match('keystring2', creds, **creds_dict)
|
||||
self.assertEqual(match, False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@ -4125,6 +4125,42 @@ class TestAuth(unittest.TestCase):
|
||||
# Assert that string passed to hmac.new is only the hash
|
||||
self.assertEqual(mock_hmac_new.call_args[0][0], key_hash)
|
||||
|
||||
def test_get_concealed_token(self):
|
||||
auth.HASH_PATH_PREFIX = 'start'
|
||||
auth.HASH_PATH_SUFFIX = 'end'
|
||||
token = 'token'
|
||||
|
||||
# Check sha512 of "start:token:end"
|
||||
hashed_token = self.test_auth._get_concealed_token(token)
|
||||
self.assertEqual(hashed_token,
|
||||
'cb320540b0b4c69eb83de2ffb80714cb6766e2d06b5579d1a35a9c4c3fb62'
|
||||
'981ec50bcc3fb94521133e69a87d1efcb83efd78f35a06b6375e410201476'
|
||||
'0722f6')
|
||||
|
||||
# Check sha512 of "start:token2:end"
|
||||
token = 'token2'
|
||||
hashed_token = self.test_auth._get_concealed_token(token)
|
||||
self.assertEqual(hashed_token,
|
||||
'ca400a6f884c168357f6af0609fda66aecd5aa613147167487495dd9f39fd'
|
||||
'8a77288568e65857294f01e398d7f14328e855f18517ccf94185d849e7f34'
|
||||
'f4259d')
|
||||
|
||||
# Check sha512 of "start2:token2:end"
|
||||
auth.HASH_PATH_PREFIX = 'start2'
|
||||
hashed_token = self.test_auth._get_concealed_token(token)
|
||||
self.assertEqual(hashed_token,
|
||||
'ad594a69f44dd6e0aad54e360b01f15bd4833ccb4dcd9116d7aba0c25fb95'
|
||||
'670155b8cc7175def7aeeb4624a0f2bb7da5f0b204a4680ea7947d3d6a045'
|
||||
'22bdde')
|
||||
|
||||
# Check sha512 of "start2:token2:end2"
|
||||
auth.HASH_PATH_SUFFIX = 'end2'
|
||||
hashed_token = self.test_auth._get_concealed_token(token)
|
||||
self.assertEqual(hashed_token,
|
||||
'446af2473ad6b28319a0fe02719a9d715b9941d12e0709851aedb4f53b890'
|
||||
'693e7f1328e68d870fe114f35f4ed9648b16a5013182db50d3d1f79a660f2'
|
||||
'0e078e')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
Loading…
x
Reference in New Issue
Block a user