# HG changeset patch # User Mads Kiilerich # Date 2015-07-07 02:09:35 # Node ID 9a02f9ef28d7955ea8681b1985e9df59732898df # Parent 3e81e6534cad077bdd2e652cb6135bd59401d5f2 utils: make API key generator more random The API key generator abused temporary filenames in what seems to be an attempt of creating keys that unambiguously specified the user and thus were unique across users. A final hashing did however remove that property. More importantly, tempfile is not documented to use secure random numbers ... and it only uses 6 characters, giving approximately 36 bits of entropy. Instead, use the cryptographically secure os.urandom directly to generate keys with the same length but with the full 160 bits of entropy. Reported and fixed by Andrew Bartlett. diff --git a/kallithea/controllers/admin/my_account.py b/kallithea/controllers/admin/my_account.py --- a/kallithea/controllers/admin/my_account.py +++ b/kallithea/controllers/admin/my_account.py @@ -260,7 +260,7 @@ class MyAccountController(BaseController if request.POST.get('del_api_key_builtin'): user = User.get(user_id) if user: - user.api_key = generate_api_key(user.username) + user.api_key = generate_api_key() Session().add(user) Session().commit() h.flash(_("Api key successfully reset"), category='success') diff --git a/kallithea/controllers/admin/users.py b/kallithea/controllers/admin/users.py --- a/kallithea/controllers/admin/users.py +++ b/kallithea/controllers/admin/users.py @@ -324,7 +324,7 @@ class UsersController(BaseController): if request.POST.get('del_api_key_builtin'): user = User.get(c.user.user_id) if user: - user.api_key = generate_api_key(user.username) + user.api_key = generate_api_key() Session().add(user) Session().commit() h.flash(_("Api key successfully reset"), category='success') diff --git a/kallithea/lib/dbmigrate/schema/db_1_2_0.py b/kallithea/lib/dbmigrate/schema/db_1_2_0.py --- a/kallithea/lib/dbmigrate/schema/db_1_2_0.py +++ b/kallithea/lib/dbmigrate/schema/db_1_2_0.py @@ -336,7 +336,7 @@ class User(Base, BaseModel): v = get_crypt_password(v) setattr(new_user, k, v) - new_user.api_key = generate_api_key(form_data['username']) + new_user.api_key = generate_api_key() Session.add(new_user) Session.commit() return new_user diff --git a/kallithea/lib/utils2.py b/kallithea/lib/utils2.py --- a/kallithea/lib/utils2.py +++ b/kallithea/lib/utils2.py @@ -32,8 +32,10 @@ import sys import time import uuid import datetime +import urllib +import binascii + import webob -import urllib import urlobject from pylons.i18n.translation import _, ungettext @@ -161,23 +163,11 @@ def detect_mode(line, default): return default -def generate_api_key(username, salt=None): +def generate_api_key(): """ - Generates unique API key for given username, if salt is not given - it'll be generated from some random string - - :param username: username as string - :param salt: salt to hash generate KEY - :rtype: str - :returns: sha1 hash from username+salt + Generates a random (presumably unique) API key. """ - from tempfile import _RandomNameSequence - import hashlib - - if salt is None: - salt = _RandomNameSequence().next() - - return hashlib.sha1(username + salt).hexdigest() + return binascii.hexlify(os.urandom(20)) def safe_int(val, default=None): diff --git a/kallithea/model/api_key.py b/kallithea/model/api_key.py --- a/kallithea/model/api_key.py +++ b/kallithea/model/api_key.py @@ -50,7 +50,7 @@ class ApiKeyModel(BaseModel): user = self._get_user(user) new_api_key = UserApiKeys() - new_api_key.api_key = generate_api_key(user.username) + new_api_key.api_key = generate_api_key() new_api_key.user_id = user.user_id new_api_key.description = description new_api_key.expires = time.time() + (lifetime * 60) if lifetime != -1 else -1 diff --git a/kallithea/model/user.py b/kallithea/model/user.py --- a/kallithea/model/user.py +++ b/kallithea/model/user.py @@ -100,7 +100,7 @@ class UserModel(BaseModel): k = 'name' setattr(new_user, k, v) - new_user.api_key = generate_api_key(form_data['username']) + new_user.api_key = generate_api_key() self.sa.add(new_user) log_create_user(new_user.get_dict(), cur_user) @@ -159,7 +159,7 @@ class UserModel(BaseModel): new_user.lastname = lastname if not edit: - new_user.api_key = generate_api_key(username) + new_user.api_key = generate_api_key() # set password only if creating an user or password is changed password_change = new_user.password and not check_password(password, diff --git a/kallithea/tests/functional/test_login.py b/kallithea/tests/functional/test_login.py --- a/kallithea/tests/functional/test_login.py +++ b/kallithea/tests/functional/test_login.py @@ -260,7 +260,7 @@ class TestLoginController(TestController new.email = email new.name = name new.lastname = lastname - new.api_key = generate_api_key(username) + new.api_key = generate_api_key() Session().add(new) Session().commit()