High severity7.5NVD Advisory· Published Aug 9, 2017· Updated May 13, 2026
CVE-2015-7764
CVE-2015-7764
Description
Lemur 0.1.4 does not use sufficient entropy in its IV when encrypting AES in CBC mode.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
lemurPyPI | < 0.1.5 | 0.1.5 |
Patches
1394e18f76e5eMerge pull request #123 from rpicard/master
7 files changed · +104 −23
docs/administration/index.rst+11 −5 modified@@ -87,17 +87,23 @@ Basic Configuration >>> secret_key = secret_key + ''.join(random.choice(string.digits) for x in range(6)) -.. data:: LEMUR_ENCRYPTION_KEY +.. data:: LEMUR_ENCRYPTION_KEYS :noindex: - The LEMUR_ENCRYPTION_KEY is used to encrypt data at rest within Lemur's database. Without this key Lemur will refuse - to start. + The LEMUR_ENCRYPTION_KEYS is used to encrypt data at rest within Lemur's database. Without a key Lemur will refuse + to start. Multiple keys can be provided to facilitate key rotation. The first key in the list is used for + encryption and all keys are tried for decryption until one works. Each key must be 32 URL safe base-64 encoded bytes. - See `LEMUR_TOKEN_SECRET` for methods of secure secret generation. + Running lemur create_config will securely generate a key for your configuration file. + If you would like to generate your own, we recommend the following method: + + >>> import os + >>> import base64 + >>> base64.urlsafe_b64encode(os.urandom(32)) :: - LEMUR_ENCRYPTION_KEY = 'supersupersecret' + LEMUR_ENCRYPTION_KEYS = ['1YeftooSbxCiX2zo8m1lXtpvQjy27smZcUUaGmffhMY=', 'LAfQt6yrkLqOK5lwpvQcT4jf2zdeTQJV1uYeh9coT5s='] Certificate Default Options
docs/faq.rst+2 −2 modified@@ -4,8 +4,8 @@ Frequently Asked Questions Common Problems --------------- -In my startup logs I see *'Aborting... Lemur cannot locate db encryption key, is LEMUR_ENCRYPTION_KEY set?'* - You likely have not correctly configured **LEMUR_ENCRYPTION_KEY**. See +In my startup logs I see *'Aborting... Lemur cannot locate db encryption key, is LEMUR_ENCRYPTION_KEYS set?'* + You likely have not correctly configured **LEMUR_ENCRYPTION_KEYS**. See :doc:`administration/index` for more information.
lemur/certificates/models.py+2 −4 modified@@ -13,9 +13,7 @@ from sqlalchemy.orm import relationship from sqlalchemy import event, Integer, ForeignKey, String, DateTime, PassiveDefault, func, Column, Text, Boolean -from sqlalchemy_utils import EncryptedType - -from lemur.utils import get_key +from lemur.utils import Vault from lemur.database import db from lemur.plugins.base import plugins @@ -213,7 +211,7 @@ class Certificate(db.Model): id = Column(Integer, primary_key=True) owner = Column(String(128)) body = Column(Text()) - private_key = Column(EncryptedType(String, get_key)) + private_key = Column(Vault) status = Column(String(128)) deleted = Column(Boolean, index=True) name = Column(String(128))
lemur/manage.py+4 −2 modified@@ -72,7 +72,7 @@ # You should consider storing these separately from your config LEMUR_TOKEN_SECRET = '{secret_token}' -LEMUR_ENCRYPTION_KEY = '{encryption_key}' +LEMUR_ENCRYPTION_KEYS = '{encryption_key}' # this is a list of domains as regexes that only admins can issue LEMUR_RESTRICTED_DOMAINS = [] @@ -171,7 +171,9 @@ def generate_settings(): settings file. """ output = CONFIG_TEMPLATE.format( - encryption_key=base64.b64encode(os.urandom(KEY_LENGTH)), + # we use Fernet.generate_key to make sure that the key length is + # compatible with Fernet + encryption_key=Fernet.generate_key(), secret_token=base64.b64encode(os.urandom(KEY_LENGTH)), flask_secret_key=base64.b64encode(os.urandom(KEY_LENGTH)), )
lemur/roles/models.py+2 −3 modified@@ -12,9 +12,8 @@ from sqlalchemy.orm import relationship from sqlalchemy import Column, Integer, String, Text, ForeignKey -from sqlalchemy_utils import EncryptedType from lemur.database import db -from lemur.utils import get_key +from lemur.utils import Vault from lemur.models import roles_users @@ -23,7 +22,7 @@ class Role(db.Model): id = Column(Integer, primary_key=True) name = Column(String(128), unique=True) username = Column(String(128)) - password = Column(EncryptedType(String, get_key)) + password = Column(Vault) description = Column(Text) authority_id = Column(Integer, ForeignKey('authorities.id')) user_id = Column(Integer, ForeignKey('users.id'))
lemur/tests/conf.py+1 −1 modified@@ -21,7 +21,7 @@ # You should consider storing these separately from your config LEMUR_TOKEN_SECRET = 'test' -LEMUR_ENCRYPTION_KEY = 'jPd2xwxgVGXONqghHNq7/S761sffYSrT3UAgKwgtMxbqa0gmKYCfag==' +LEMUR_ENCRYPTION_KEYS = 'o61sBLNBSGtAckngtNrfVNd8xy8Hp9LBGDstTbMbqCY=' # this is a list of domains as regexes that only admins can issue LEMUR_RESTRICTED_DOMAINS = []
lemur/utils.py+82 −6 modified@@ -5,17 +5,93 @@ :license: Apache, see LICENSE for more details. .. moduleauthor:: Kevin Glisson <kglisson@netflix.com> """ +import six from flask import current_app +from cryptography.fernet import Fernet, MultiFernet +import sqlalchemy.types as types -def get_key(): +def get_keys(): """ - Gets the current encryption key + Gets the encryption keys. + + This supports multiple keys to facilitate key rotation. The first + key in the list is used to encrypt. Decryption is attempted with + each key in succession. :return: """ + + # when running lemur create_config, this code needs to work despite + # the fact that there is not a current_app with a config at that point try: - return current_app.config.get('LEMUR_ENCRYPTION_KEY').strip() - except RuntimeError: - print("No Encryption Key Found") - return '' + keys = current_app.config.get('LEMUR_ENCRYPTION_KEYS') + except: + print("no encryption keys") + return [] + + # this function is expected to return a list of keys, but we want + # to let people just specify a single key + if not isinstance(keys, list): + keys = [keys] + + # make sure there is no accidental whitespace + keys = [key.strip() for key in keys] + + return keys + + +class Vault(types.TypeDecorator): + """ + A custom SQLAlchemy column type that transparently handles encryption. + + This uses the MultiFernet from the cryptography package to faciliate + key rotation. That class handles encryption and signing. + + Fernet uses AES in CBC mode with 128-bit keys and PKCS7 padding. It + uses HMAC-SHA256 for ciphertext authentication. Initialization + vectors are generated using os.urandom(). + """ + + # required by SQLAlchemy. defines the underlying column type + impl = types.Binary + + def process_bind_param(self, value, dialect): + """ + Encrypt values on the way into the database. + + MultiFernet.encrypt uses the first key in the list. + """ + + # we assume that the user's keys are already Fernet keys (32 byte + # keys that have been base64 encoded). + self.keys = [Fernet(key) for key in get_keys()] + + # we only support strings and they should be of type bytes for Fernet + if not isinstance(value, six.string_types): + return None + + value = bytes(value) + + return MultiFernet(self.keys).encrypt(value) + + def process_result_value(self, value, dialect): + """ + Decrypt values on the way out of the database. + + MultiFernet tries each key until one works. + """ + + # we assume that the user's keys are already Fernet keys (32 byte + # keys that have been base64 encoded). + self.keys = [Fernet(key) for key in get_keys()] + + # if the value is not a string we aren't going to try to decrypt + # it. this is for the case where the column is null + if not isinstance(value, six.string_types): + return None + + # TODO this may raise an InvalidToken exception in certain + # cases. Should we handle that? + # https://cryptography.io/en/latest/fernet/#cryptography.fernet.Fernet.decrypt + return MultiFernet(self.keys).decrypt(value)
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
7- www.openwall.com/lists/oss-security/2015/10/20/3nvdMailing ListThird Party AdvisoryWEB
- github.com/Netflix/lemur/issues/117nvdThird Party AdvisoryWEB
- github.com/advisories/GHSA-chg9-3c3p-ch23ghsaADVISORY
- github.com/kvesteri/sqlalchemy-utils/issues/166nvdThird Party AdvisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2015-7764ghsaADVISORY
- github.com/Netflix/lemur/commit/394e18f76e5eb534d95160945ebc231ec3b4c794ghsaWEB
- github.com/pypa/advisory-database/tree/main/vulns/lemur/PYSEC-2017-50.yamlghsaWEB
News mentions
0No linked articles in our index yet.