More random values for oAuth1 verifier

The oAuth1 verifier was generated as a random number ranging from
1000 to 9999. This small range of numbers is vulnerable to
brute-force attacks as described in CWE-330. The verifier is now
a 8-character long alphanumerical string, a good compromise between
security against guessing and ease of use.

SecurityImpact
Change-Id: Ibe4a2e57a02c261d85ba6c0d61696f134c54443e
Closes-Bug: #1236675
This commit is contained in:
Matthieu Huin 2014-04-22 17:14:25 +02:00
parent 314c0325fc
commit fd02a9c3d0
4 changed files with 13 additions and 5 deletions

View File

@ -204,7 +204,8 @@ class OAuth1(object):
token_ref = self._get_request_token(session, request_token_id)
token_dict = token_ref.to_dict()
token_dict['authorizing_user_id'] = user_id
token_dict['verifier'] = str(random.randint(1000, 9999))
token_dict['verifier'] = ''.join(random.sample(core.VERIFIER_CHARS,
8))
token_dict['role_ids'] = jsonutils.dumps(role_ids)
new_token = RequestToken.from_dict(token_dict)

View File

@ -17,6 +17,7 @@
from __future__ import absolute_import
import abc
import string
import oauthlib.common
from oauthlib import oauth1
@ -39,6 +40,11 @@ AuthorizationEndpoint = oauth1.AuthorizationEndpoint
SIG_HMAC = oauth1.SIGNATURE_HMAC
RequestTokenEndpoint = oauth1.RequestTokenEndpoint
oRequest = oauthlib.common.Request
# The characters used to generate verifiers are limited to alphanumerical
# values for ease of manual entry. Commonly confused characters are omitted.
VERIFIER_CHARS = string.ascii_letters + string.digits
CONFUSED_CHARS = 'jiIl1oO0'
VERIFIER_CHARS = ''.join(c for c in VERIFIER_CHARS if c not in CONFUSED_CHARS)
class Token(object):

View File

@ -58,10 +58,8 @@ class OAuthValidator(oauth1.RequestValidator):
return set(nonce) <= self.safe_characters
def check_verifier(self, verifier):
try:
return 1000 <= int(verifier) <= 9999
except ValueError:
return False
return (all(i in oauth1.VERIFIER_CHARS for i in verifier) and
len(verifier) == 8)
def get_client_secret(self, client_key, request):
client = self.oauth_api.get_consumer_with_secret(client_key)

View File

@ -20,6 +20,7 @@ from six.moves import urllib
from keystone import config
from keystone.contrib import oauth1
from keystone.contrib.oauth1 import controllers
from keystone.contrib.oauth1 import core
from keystone import exception
from keystone.tests import test_v3
@ -256,6 +257,8 @@ class OAuthFlowTests(OAuth1Tests):
body = {'roles': [{'id': self.role_id}]}
resp = self.put(url, body=body, expected_status=200)
self.verifier = resp.result['token']['oauth_verifier']
self.assertTrue(all(i in core.VERIFIER_CHARS for i in self.verifier))
self.assertEqual(8, len(self.verifier))
self.request_token.set_verifier(self.verifier)
url, headers = self._create_access_token(self.consumer,