python.lang.security.audit.md5-used-as-password.md5-used-as-password

profile photo of semgrepsemgrep
Author
unknown
Download Count*

It looks like MD5 is used as a password hash. MD5 is not considered a secure password hash because it can be cracked by an attacker in a short amount of time. Use a suitable password hashing function such as scrypt. You can use hashlib.scrypt.

Run Locally

Run in CI

Defintion

rules:
  - id: md5-used-as-password
    severity: WARNING
    message: It looks like MD5 is used as a password hash. MD5 is not considered a
      secure password hash because it can be cracked by an attacker in a short
      amount of time. Use a suitable password hashing function such as scrypt.
      You can use `hashlib.scrypt`.
    languages:
      - python
    metadata:
      cwe:
        - "CWE-327: Use of a Broken or Risky Cryptographic Algorithm"
      owasp:
        - A03:2017 - Sensitive Data Exposure
        - A02:2021 - Cryptographic Failures
      references:
        - https://tools.ietf.org/html/rfc6151
        - https://crypto.stackexchange.com/questions/44151/how-does-the-flame-malware-take-advantage-of-md5-collision
        - https://pycryptodome.readthedocs.io/en/latest/src/hash/sha3_256.html
        - https://security.stackexchange.com/questions/211/how-to-securely-hash-passwords
        - https://github.com/returntocorp/semgrep-rules/issues/1609
        - https://docs.python.org/3/library/hashlib.html#hashlib.scrypt
      category: security
      technology:
        - pycryptodome
        - hashlib
        - md5
      subcategory:
        - vuln
      likelihood: HIGH
      impact: LOW
      confidence: MEDIUM
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Cryptographic Issues
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern: hashlib.md5
              - pattern: hashlib.new(..., name="MD5", ...)
              - pattern: Cryptodome.Hash.MD5
              - pattern: Crypto.Hash.MD5
              - pattern: cryptography.hazmat.primitives.hashes.MD5
    pattern-sinks:
      - patterns:
          - pattern: $FUNCTION(...)
          - metavariable-regex:
              metavariable: $FUNCTION
              regex: (?i)(.*password.*)

Examples

md5-used-as-password.py

import hashlib
from cryptography.hazmat.primitives import hashes
from Crypto.Hash import MD5, SHA256

#### True Positives ####
def ex1(user, pwtext):
    md5 = hashlib.md5(pwtext).hexdigest()
    # ruleid: md5-used-as-password
    user.setPassword(md5)

def ex2(user, pwtext):
    digest = hashes.Hash(hashes.MD5())
    digest.update(bytes(pwtext))
    # ruleid: md5-used-as-password
    user.setPassword(digest.finalize())

def ex3(user, pwtext):
    h = MD5.new()
    h.update(bytes(pwtext))
    # ruleid: md5-used-as-password
    user.setPassword(h.hexdigest())

#### True Negatives ####
def ok1(user, pwtext):
    sha = hashlib.sha256(pwtext).hexdigest()
    # ok: md5-used-as-password
    user.setPassword(sha)

def ok2(user, pwtext):
    digest = hashes.Hash(hashes.SHA256())
    digest.update(bytes(pwtext))
    # ok: md5-used-as-password
    user.setPassword(digest.finalize())

def ok3(user, pwtext):
    h = SHA256.new()
    h.update(bytes(pwtext))
    # ok: md5-used-as-password
    user.setPassword(h.hexdigest())

def ok4(user, pwtext):
    h = MD5.new()
    h.update(bytes(pwtext))
    # ok: md5-used-as-password
    user.updateSomethingElse(h.hexdigest())