Skip to content

Commit

Permalink
Better hashlib check for Python 3.9 (#805)
Browse files Browse the repository at this point in the history
* Better hashlib check for Python 3.9

In Python 3.9 and later, the hashlib function has a new keyword
argument usedforsecurity to describe the usage of the hash. In
that way, we can better identify the severity of the error.

Previously, hashlib.md5 and the like were part of the blacklist
check. For Python 3.9, it'll be part of the hashlib plugin so
it can do more advanced checking of usedforsecurity.

Signed-off-by: Eric Brown <browne@vmware.com>

* Update hashlib_insecure_functions.py
  • Loading branch information
ericwb committed Feb 10, 2022
1 parent a9eaafa commit c4372a0
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 126 deletions.
68 changes: 47 additions & 21 deletions bandit/blacklists/calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@
+------+---------------------+------------------------------------+-----------+
"""
import sys

from bandit.blacklists import utils
from bandit.core import issue

Expand Down Expand Up @@ -362,28 +364,52 @@ def gen_blacklist():
)
)

sets.append(
utils.build_conf_dict(
"md5",
"B303",
issue.Cwe.BROKEN_CRYPTO,
[
"hashlib.md5",
"hashlib.sha1",
"Crypto.Hash.MD2.new",
"Crypto.Hash.MD4.new",
"Crypto.Hash.MD5.new",
"Crypto.Hash.SHA.new",
"Cryptodome.Hash.MD2.new",
"Cryptodome.Hash.MD4.new",
"Cryptodome.Hash.MD5.new",
"Cryptodome.Hash.SHA.new",
"cryptography.hazmat.primitives.hashes.MD5",
"cryptography.hazmat.primitives.hashes.SHA1",
],
"Use of insecure MD2, MD4, MD5, or SHA1 hash function.",
if sys.version_info >= (3, 9):
sets.append(
utils.build_conf_dict(
"md5",
"B303",
issue.Cwe.BROKEN_CRYPTO,
[
"Crypto.Hash.MD2.new",
"Crypto.Hash.MD4.new",
"Crypto.Hash.MD5.new",
"Crypto.Hash.SHA.new",
"Cryptodome.Hash.MD2.new",
"Cryptodome.Hash.MD4.new",
"Cryptodome.Hash.MD5.new",
"Cryptodome.Hash.SHA.new",
"cryptography.hazmat.primitives.hashes.MD5",
"cryptography.hazmat.primitives.hashes.SHA1",
],
"Use of insecure MD2, MD4, MD5, or SHA1 hash function.",
)
)
else:
sets.append(
utils.build_conf_dict(
"md5",
"B303",
issue.Cwe.BROKEN_CRYPTO,
[
"hashlib.md4",
"hashlib.md5",
"hashlib.sha",
"hashlib.sha1",
"Crypto.Hash.MD2.new",
"Crypto.Hash.MD4.new",
"Crypto.Hash.MD5.new",
"Crypto.Hash.SHA.new",
"Cryptodome.Hash.MD2.new",
"Cryptodome.Hash.MD4.new",
"Cryptodome.Hash.MD5.new",
"Cryptodome.Hash.SHA.new",
"cryptography.hazmat.primitives.hashes.MD5",
"cryptography.hazmat.primitives.hashes.SHA1",
],
"Use of insecure MD2, MD4, MD5, or SHA1 hash function.",
)
)
)

sets.append(
utils.build_conf_dict(
Expand Down
118 changes: 118 additions & 0 deletions bandit/plugins/hashlib_insecure_functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#
# SPDX-License-Identifier: Apache-2.0
r"""
======================================================================
B324: Test use of insecure md4, md5, or sha1 hash functions in hashlib
======================================================================
This plugin checks for the usage of the insecure MD4, MD5, or SHA1 hash
functions in ``hashlib``. The ``hashlib.new`` function provides
the ability to construct a new hashing object using the named algorithm. This
can be used to create insecure hash functions like MD4 and MD5 if they are
passed as algorithm names to this function.
For Python versions prior to 3.9, this check is similar to B303 blacklist
except that this checks for insecure hash functions created using
``hashlib.new`` function. For Python version 3.9 and later, this check
does additional checking for usage of keyword usedforsecurity on all
function variations of hashlib.
:Example:
>> Issue: [B324:hashlib] Use of weak MD2, MD4, MD5, or SHA1 hash for
security. Consider usedforsecurity=False
Severity: High Confidence: High
CWE: CWE-327 (https://cwe.mitre.org/data/definitions/327.html)
Location: examples/hashlib_new_insecure_functions.py:3:0
More Info: https://bandit.readthedocs.io/en/latest/plugins/b324_hashlib.html
2
3 hashlib.new('md5')
4
.. seealso::
- https://cwe.mitre.org/data/definitions/327.html
.. versionadded:: 1.5.0
.. versionchanged:: 1.7.3
CWE information added
""" # noqa: E501
import sys

import bandit
from bandit.core import issue
from bandit.core import test_properties as test


def _hashlib_func(context):
if isinstance(context.call_function_name_qual, str):
qualname_list = context.call_function_name_qual.split(".")

if "hashlib" in qualname_list:
func = qualname_list[-1]
args = context.call_args
keywords = context.call_keywords
name = args[0] if args else keywords["name"]

if func in ("md4", "md5", "sha", "sha1"):
if keywords.get("usedforsecurity", "True") == "True":
return bandit.Issue(
severity=bandit.HIGH,
confidence=bandit.HIGH,
cwe=issue.Cwe.BROKEN_CRYPTO,
text="Use of weak MD2, MD4, MD5, or SHA1 hash "
"for security. Consider usedforsecurity=False",
lineno=context.node.lineno,
)
elif func == "new":
if isinstance(name, str) and name.lower() in (
"md4",
"md5",
"sha",
"sha1",
):
if keywords.get("usedforsecurity", "True") == "True":
return bandit.Issue(
severity=bandit.HIGH,
confidence=bandit.HIGH,
cwe=issue.Cwe.BROKEN_CRYPTO,
text="Use of weak MD2, MD4, MD5, or SHA1 hash "
"for security. Consider usedforsecurity=False",
lineno=context.node.lineno,
)


def _hashlib_new(context):
if isinstance(context.call_function_name_qual, str):
qualname_list = context.call_function_name_qual.split(".")
func = qualname_list[-1]

if "hashlib" in qualname_list and func == "new":
args = context.call_args
keywords = context.call_keywords
name = args[0] if args else keywords["name"]
if isinstance(name, str) and name.lower() in (
"md4",
"md5",
"sha",
"sha1",
):
return bandit.Issue(
severity=bandit.MEDIUM,
confidence=bandit.HIGH,
cwe=issue.Cwe.BROKEN_CRYPTO,
text="Use of insecure MD2, MD4, MD5, or SHA1 hash "
"function.",
lineno=context.node.lineno,
)


@test.test_id("B324")
@test.checks("Call")
def hashlib(context):
if sys.version_info >= (3, 9):
return _hashlib_func(context)
else:
return _hashlib_new(context)
85 changes: 0 additions & 85 deletions bandit/plugins/hashlib_new_insecure_functions.py

This file was deleted.

2 changes: 2 additions & 0 deletions examples/crypto-md5.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

hashlib.sha1(1)

hashlib.sha1(usedforsecurity=False)

pycrypto_md2.new()
pycrypto_md4.new()
pycrypto_md5.new()
Expand Down
6 changes: 0 additions & 6 deletions examples/hashlib_new_insecure_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@

hashlib.new(string='test', name='MD5')

# 3rd arg only availabe in Python 3.9+
hashlib.new('md5', b'test', True)

hashlib.new('sha1')

hashlib.new(string='test', name='SHA1')
Expand All @@ -29,8 +26,5 @@

hashlib.new('SHA512')

# 3rd arg only availabe in Python 3.9+
hashlib.new('md5', b'test', False)

# usedforsecurity arg only availabe in Python 3.9+
hashlib.new(name='sha1', usedforsecurity=False)
4 changes: 2 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ bandit.plugins =
# bandit/plugins/injection_sql.py
hardcoded_sql_expressions = bandit.plugins.injection_sql:hardcoded_sql_expressions

# bandit/plugins/hashlib_new_insecure_functions.py
hashlib_new_insecure_functions = bandit.plugins.hashlib_new_insecure_functions:hashlib_new
# bandit/plugins/hashlib_insecure_functions.py
hashlib_insecure_functions = bandit.plugins.hashlib_insecure_functions:hashlib

# bandit/plugins/injection_wildcard.py
linux_commands_wildcard_injection = bandit.plugins.injection_wildcard:linux_commands_wildcard_injection
Expand Down

0 comments on commit c4372a0

Please sign in to comment.