Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better hashlib check for Python 3.9 #805

Merged
merged 2 commits into from Feb 10, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
68 changes: 47 additions & 21 deletions bandit/blacklists/calls.py
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
@@ -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 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
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
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
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