Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion vulnerable_weak_crypto.py
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Hash algorithm mismatch between hash_password_weak (now SHA256) and verify_password (still MD5)

The PR updated hash_password_weak from MD5 to SHA256, but the corresponding verify_password function at vulnerable_weak_crypto.py:40 still uses hashlib.md5 to compute the hash for comparison. If a password is stored using hash_password_weak (SHA256) and later verified using verify_password (MD5), the hashes will never match, causing all password verifications to fail.

(Refers to line 40)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from Crypto.Hash import MD5, SHA1

def hash_password_weak(password):
return hashlib.md5(password.encode()).hexdigest()
return hashlib.sha256(password.encode()).hexdigest()

Check failure

Code scanning / CodeQL

Use of a broken or weak cryptographic hashing algorithm on sensitive data High

Sensitive data (password)
is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function.

Copilot Autofix

AI 2 months ago

In general, password hashing must use a dedicated, slow, memory-hard password hashing scheme such as Argon2, scrypt, bcrypt, or PBKDF2, rather than a fast general-purpose hash like SHA-256. These algorithms also incorporate salts and parameters (iterations, memory cost, parallelism) to make brute-force attacks impractical.

For this specific code, the minimal-impact fix is to change hash_password_weak to use a standard password hashing primitive while preserving the function’s interface (accept a plaintext password, return a string hash). A good choice with no extra application code is PBKDF2 via hashlib.pbkdf2_hmac, which is part of the Python standard library and thus doesn’t introduce nonstandard dependencies. To do this, we will:

  • Add imports for os and base64 at the top of vulnerable_weak_crypto.py (or reuse them if already present) so we can generate a random salt and return a safe string representation.
  • Replace the body of hash_password_weak to:
    • Generate a random 16-byte salt using os.urandom(16).
    • Derive a 32-byte key using hashlib.pbkdf2_hmac('sha256', password.encode(), salt, iterations), with a reasonable iteration count (for example, 100_000 as commonly recommended).
    • Encode the salt and derived key with base64.b64encode and join them with a separator (e.g., "salt$hash") into a single string for storage/verification.
  • Leave the rest of the file (including other weak constructs) untouched, since the CodeQL finding we’re addressing specifically concerns the use of SHA-256 for password hashing at line 7.

This preserves the outward behavior (input: password string; output: hash string) while making the hashing algorithm computationally expensive and salted.

Suggested changeset 1
vulnerable_weak_crypto.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/vulnerable_weak_crypto.py b/vulnerable_weak_crypto.py
--- a/vulnerable_weak_crypto.py
+++ b/vulnerable_weak_crypto.py
@@ -2,9 +2,17 @@
 import random
 from Crypto.Cipher import DES, ARC2, Blowfish
 from Crypto.Hash import MD5, SHA1
+import os
+import base64
 
 def hash_password_weak(password):
-    return hashlib.sha256(password.encode()).hexdigest()
+    # Use a computationally expensive, salted hash (PBKDF2) instead of raw SHA-256
+    salt = os.urandom(16)
+    iterations = 100_000
+    dk = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, iterations, dklen=32)
+    salt_b64 = base64.b64encode(salt).decode('ascii')
+    dk_b64 = base64.b64encode(dk).decode('ascii')
+    return f"{iterations}${salt_b64}${dk_b64}"
 
 def hash_with_sha1(data):
     return hashlib.sha1(data.encode()).hexdigest()
EOF
@@ -2,9 +2,17 @@
import random
from Crypto.Cipher import DES, ARC2, Blowfish
from Crypto.Hash import MD5, SHA1
import os
import base64

def hash_password_weak(password):
return hashlib.sha256(password.encode()).hexdigest()
# Use a computationally expensive, salted hash (PBKDF2) instead of raw SHA-256
salt = os.urandom(16)
iterations = 100_000
dk = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, iterations, dklen=32)
salt_b64 = base64.b64encode(salt).decode('ascii')
dk_b64 = base64.b64encode(dk).decode('ascii')
return f"{iterations}${salt_b64}${dk_b64}"

def hash_with_sha1(data):
return hashlib.sha1(data.encode()).hexdigest()
Copilot is powered by AI and may make mistakes. Always verify output.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hash algorithm mismatch between password hashing and verification

High Severity

hash_password_weak now produces SHA-256 hashes, but verify_password on line 40 still computes an MD5 hash for comparison. Any password hashed with hash_password_weak will never match when checked by verify_password, since the two algorithms produce different digests. The verification function needs to use the same hashlib.sha256 algorithm.

Additional Locations (1)
Fix in Cursor Fix in Web


def hash_with_sha1(data):
return hashlib.sha1(data.encode()).hexdigest()
Expand Down
Loading