CVE-2017-16613
Description
An issue was discovered in middleware.py in OpenStack Swauth through 1.2.0 when used with OpenStack Swift through 2.15.1. The Swift object store and proxy server are saving (unhashed) tokens retrieved from the Swauth middleware authentication mechanism to a log file as part of a GET URI. This allows attackers to bypass authentication by inserting a token into an X-Auth-Token header of a new request. NOTE: github.com/openstack/swauth URLs do not mean that Swauth is maintained by an official OpenStack project team.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
swauthPyPI | < 1.3.0 | 1.3.0 |
Affected products
3- cpe:2.3:o:debian:debian_linux:9.0:*:*:*:*:*:*:*
Patches
170af7986265aHash token before storing it in Swift
3 files changed · +59 −5
swauth/middleware.py+22 −5 modified@@ -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.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 @@ def __call__(self, env, start_response): 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 @@ def get_groups(self, env, token): 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 @@ def handle_delete_user(self, req): (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 @@ def handle_get_token(self, req): 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 @@ def handle_get_token(self, req): # 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 @@ def handle_validate_token(self, req): 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:
test/unit/test_authtypes.py+1 −0 modified@@ -202,5 +202,6 @@ def test_sha512_invalid_match(self): match = self.auth_encoder.match('keystring2', creds, **creds_dict) self.assertEqual(match, False) + if __name__ == '__main__': unittest.main()
test/unit/test_middleware.py+36 −0 modified@@ -4125,6 +4125,42 @@ def test_s3_only_hash_passed_to_hmac(self): # 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()
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- bugs.debian.org/cgi-bin/bugreport.cginvdIssue TrackingPatchThird Party AdvisoryWEB
- bugs.launchpad.net/swift/+bug/1655781nvdIssue TrackingPatchThird Party AdvisoryWEB
- github.com/openstack/swauth/commit/70af7986265a3defea054c46efc82d0698917298nvdIssue TrackingPatchThird Party AdvisoryWEB
- www.securityfocus.com/bid/101926nvdThird Party AdvisoryVDB Entry
- github.com/advisories/GHSA-qhq8-xwqv-pvv9ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2017-16613ghsaADVISORY
- www.debian.org/security/2017/dsa-4044nvdIssue TrackingThird Party AdvisoryWEB
- github.com/pypa/advisory-database/tree/main/vulns/swauth/PYSEC-2017-84.yamlghsaWEB
- web.archive.org/web/20200227140059/http://www.securityfocus.com/bid/101926ghsaWEB
News mentions
0No linked articles in our index yet.