| name | crypto-audit |
| description | Use when reviewing code for weak encryption, hardcoded cryptographic keys, insecure TLS/SSL configuration, broken hashing, bad randomness, or any cryptographic implementation concern — regardless of language. |
Crypto Audit
This skill performs static code analysis for cryptographic vulnerabilities across JavaScript/TypeScript, Python, Go, Java, and Rust projects. It identifies 12 common crypto anti-patterns — weak algorithms, hardcoded keys, insecure randomness, insufficient key sizes, and more — mapping each finding to CWE and OWASP Top 10:2021 standards with concrete UNSAFE/SAFE code pairs for remediation.
When to Use
- When the user asks to "audit crypto", "review cryptographic code", or "check for weak encryption"
- When the user mentions "crypto audit", "cryptographic review", or "insecure crypto"
- When scanning code that imports cryptographic libraries (e.g.,
crypto, hashlib, javax.crypto, crypto/tls)
- When reviewing code for compliance with cryptographic standards (FIPS, PCI-DSS)
- When a pull request modifies encryption, hashing, TLS configuration, or key management code
- When the user asks about "hardcoded keys", "weak hashing", "insecure random", or "deprecated TLS"
When NOT to Use
- When the user is asking about non-cryptographic security issues (use
bandit-sast or security-review)
- When the user wants runtime TLS scanning of live servers (use a DAST tool like
testssl.sh)
- When reviewing general code quality unrelated to cryptography
- When the
security-review skill already covers the request at a general level
- When the user is asking about SQL injection, API security, or input validation — you MUST decline and recommend
api-security-tester, security-review, or bandit-sast
- When the user wants to scan dependencies for supply chain issues — you MUST decline and recommend
socket-sca
Prerequisites
Tool Installed (Preferred)
No external tool required. This skill uses code analysis only.
All 12 checks are performed through pattern matching and code inspection — no CLI tool needs to be installed, configured, or invoked.
Tool Not Installed (Fallback)
This skill is always available as a pure analysis skill. There is no fallback mode because there is no external tool dependency. All checks run directly through code analysis.
Workflow
- Detect project languages — Inspect project files to determine which languages are in use:
package.json or *.ts/*.js (JavaScript/TypeScript), requirements.txt/*.py (Python), go.mod/*.go (Go), pom.xml/*.java (Java), Cargo.toml/*.rs (Rust).
- Identify crypto-relevant files — Search for files that import cryptographic modules:
- JavaScript/TypeScript:
require('crypto'), import crypto, require('node-forge')
- Python:
import hashlib, from cryptography, from Crypto, import ssl
- Go:
import "crypto/, import "crypto/tls", import "crypto/rsa"
- Java:
import javax.crypto, import java.security, import javax.net.ssl
- Rust:
use ring::, use openssl::, use aes::, use sha2::
- Run the 12 crypto anti-pattern checks against each identified file (see Checks section below).
- For each finding:
a. Determine severity (Critical / High / Medium / Low) using the Reference Tables
b. Map to the relevant CWE identifier
c. Map to the relevant OWASP Top 10:2021 category
d. Record file path and line number
e. Generate the UNSAFE pattern found and the corresponding SAFE fix
f. Draft a remediation recommendation
- Deduplicate and sort findings by severity: Critical > High > Medium > Low.
- Generate the findings report using the Findings Format below.
- Summarize — State total findings, breakdown by severity, and top 3 remediation priorities.
Checks
Check 1: Weak Hash Algorithms (MD5, SHA1) for Security Purposes
CWE-328 (Use of Weak Hash) | A02:2021 - Cryptographic Failures | Severity: High
WHY: MD5 and SHA1 are cryptographically broken. MD5 collisions can be generated in seconds; SHA1 collisions have been demonstrated practically (SHAttered attack). Using them for password hashing, integrity verification, or digital signatures allows attackers to forge data or crack passwords.
UNSAFE:
const crypto = require('crypto');
const hash = crypto.createHash('md5').update(password).digest('hex');
import hashlib
token = hashlib.sha1(user_id.encode()).hexdigest()
import "crypto/md5"
h := md5.Sum(data)
import java.security.MessageDigest;
MessageDigest md = MessageDigest.getInstance("MD5");
SAFE:
const crypto = require('crypto');
const hash = crypto.createHash('sha256').update(data).digest('hex');
const bcrypt = require('bcrypt');
const hashed = await bcrypt.hash(password, 12);
import hashlib
digest = hashlib.sha256(data).hexdigest()
import bcrypt
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
Check 2: Hardcoded Cryptographic Keys and IVs
CWE-321 (Use of Hard-coded Cryptographic Key) | A02:2021 - Cryptographic Failures | Severity: Critical
WHY: Hardcoded keys are extractable from source code, version control, or compiled binaries. Anyone with access to the codebase can decrypt all data encrypted with that key. Key rotation becomes impossible without redeploying code.
UNSAFE:
from Crypto.Cipher import AES
KEY = b'mysecretkey12345'
IV = b'0000000000000000'
cipher = AES.new(KEY, AES.MODE_CBC, IV)
const key = 'hardcoded-secret-key-1234567890ab';
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
byte[] keyBytes = "MySuperSecretKey".getBytes();
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
SAFE:
import os
from Crypto.Cipher import AES
KEY = os.environ['ENCRYPTION_KEY'].encode()
IV = os.urandom(16)
cipher = AES.new(KEY, AES.MODE_CBC, IV)
const key = Buffer.from(process.env.ENCRYPTION_KEY, 'hex');
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
Check 3: Insecure Random Number Generation
CWE-330 (Use of Insufficiently Random Values) | A02:2021 - Cryptographic Failures | Severity: High
WHY: Non-cryptographic PRNGs like Math.random(), Python's random.random(), and Go's math/rand produce predictable output. An attacker who knows or guesses the seed can reproduce the entire sequence, compromising session tokens, OTPs, password reset tokens, and nonces.
UNSAFE:
const sessionId = Math.random().toString(36).substring(2);
const otp = Math.floor(Math.random() * 1000000).toString().padStart(6, '0');
import random
token = ''.join(random.choices('abcdef0123456789', k=32))
import "math/rand"
token := rand.Intn(999999)
SAFE:
const crypto = require('crypto');
const sessionId = crypto.randomBytes(32).toString('hex');
import secrets
token = secrets.token_hex(32)
otp = secrets.randbelow(1000000)
import "crypto/rand"
import "math/big"
n, _ := rand.Int(rand.Reader, big.NewInt(999999))
Check 4: Weak Key Sizes
CWE-326 (Inadequate Encryption Strength) | A02:2021 - Cryptographic Failures | Severity: High
WHY: Short keys reduce the cost of brute-force attacks. RSA keys under 2048 bits can be factored with modern hardware. AES-128 is acceptable but AES-256 is recommended for long-term security. EC keys under 256 bits are insufficient.
UNSAFE:
import "crypto/rsa"
key, _ := rsa.GenerateKey(rand.Reader, 1024)
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
from cryptography.hazmat.primitives.asymmetric import rsa
private_key = rsa.generate_private_key(public_exponent=65537, key_size=1024)
SAFE:
key, _ := rsa.GenerateKey(rand.Reader, 4096)
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(4096);
private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096)
Check 5: AES in ECB Mode
CWE-327 (Use of a Broken or Risky Cryptographic Algorithm) | A02:2021 - Cryptographic Failures | Severity: High
WHY: ECB mode encrypts each block independently, so identical plaintext blocks produce identical ciphertext blocks. This leaks patterns in the data (the "ECB penguin" problem) and makes the ciphertext vulnerable to block swapping and replay attacks.
UNSAFE:
import javax.crypto.Cipher;
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
from Crypto.Cipher import AES
cipher = AES.new(key, AES.MODE_ECB)
block, _ := aes.NewCipher(key)
block.Encrypt(dst, src)
SAFE:
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, spec);
from Crypto.Cipher import AES
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
ciphertext, tag = cipher.encrypt_and_digest(plaintext)
Check 6: Missing HMAC Verification (Verify Before Decrypt)
CWE-327 (Use of a Broken or Risky Cryptographic Algorithm) | A02:2021 - Cryptographic Failures | Severity: Medium
WHY: Decrypting without first verifying message integrity enables padding oracle attacks, bit-flipping attacks, and chosen-ciphertext attacks. The decrypt-then-verify pattern (or skipping verification entirely) allows attackers to manipulate ciphertext and recover plaintext.
UNSAFE:
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
let plaintext = decipher.update(ciphertext, 'hex', 'utf8');
plaintext += decipher.final('utf8');
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext)
SAFE:
const hmac = crypto.createHmac('sha256', hmacKey);
hmac.update(ciphertext);
const computedMac = hmac.digest();
if (!crypto.timingSafeEqual(computedMac, receivedMac)) {
throw new Error('HMAC verification failed — ciphertext tampered');
}
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
plaintext = cipher.decrypt_and_verify(ciphertext, tag)
Check 7: IV Reuse / Static IV
CWE-329 (Generation of Predictable IV with CBC Mode) | A02:2021 - Cryptographic Failures | Severity: High
WHY: Reusing an IV with the same key in CBC mode leaks information about whether two plaintexts share a common prefix. In CTR/GCM mode, IV reuse is catastrophic — it allows XOR of plaintexts and complete key recovery via nonce-misuse attacks.
UNSAFE:
const iv = Buffer.from('0000000000000000');
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
IV = b'\x00' * 16
cipher = AES.new(key, AES.MODE_CBC, IV)
byte[] iv = new byte[16];
IvParameterSpec ivSpec = new IvParameterSpec(iv);
SAFE:
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
iv = os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
SecureRandom random = new SecureRandom();
byte[] iv = new byte[16];
random.nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Check 8: Improper Certificate Validation
CWE-295 (Improper Certificate Validation) | A07:2021 - Security Misconfiguration | Severity: Critical
WHY: Disabling TLS certificate verification allows man-in-the-middle attacks. An attacker on the network can intercept, read, and modify all traffic — even though it is "encrypted" — because the client accepts any certificate, including the attacker's.
UNSAFE:
import requests
response = requests.get('https://api.example.com', verify=False)
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
const agent = new https.Agent({ rejectUnauthorized: false });
import "crypto/tls"
tlsConfig := &tls.Config{
InsecureSkipVerify: true,
}
TrustManager[] trustAll = new TrustManager[]{
new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
public X509Certificate[] getAcceptedIssuers() { return null; }
}
};
SAFE:
response = requests.get('https://api.example.com')
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS12,
}
Check 9: Timing Attacks via Non-Constant-Time Comparison
CWE-208 (Observable Timing Discrepancy) | A02:2021 - Cryptographic Failures | Severity: Medium
WHY: Comparing secrets with == or === short-circuits on the first differing byte, leaking information about how many leading bytes are correct. Over many requests, an attacker can reconstruct the secret byte-by-byte (e.g., HMAC tokens, API keys, password hashes).
UNSAFE:
if (computedHmac === receivedHmac) {
}
if computed_hmac == received_hmac:
pass
if bytes.Equal(computedMAC, receivedMAC) {
}
SAFE:
const crypto = require('crypto');
if (crypto.timingSafeEqual(Buffer.from(computedHmac), Buffer.from(receivedHmac))) {
}
import hmac
if hmac.compare_digest(computed_hmac, received_hmac):
pass
import "crypto/subtle"
if subtle.ConstantTimeCompare(computedMAC, receivedMAC) == 1 {
}
Check 10: Deprecated TLS Versions
CWE-327 (Use of a Broken or Risky Cryptographic Algorithm) | A07:2021 - Security Misconfiguration | Severity: High
WHY: TLS 1.0 and 1.1 have known vulnerabilities (BEAST, POODLE, Lucky13). SSLv3 is completely broken. PCI DSS, NIST, and major browsers have deprecated these versions. Using them exposes traffic to downgrade attacks and known protocol-level exploits.
UNSAFE:
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS10,
}
import ssl
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv3)
SSLContext ctx = SSLContext.getInstance("TLSv1");
const tls = require('tls');
const options = { secureProtocol: 'TLSv1_method' };
SAFE:
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS12,
}
import ssl
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
Check 11: Weak Password Hashing
CWE-916 (Use of Password Hash With Insufficient Computational Effort) | A02:2021 - Cryptographic Failures | Severity: Critical
WHY: Plain SHA-256/SHA-512 and MD5 are fast general-purpose hashes — a modern GPU can compute billions per second. Password hashing requires intentionally slow, memory-hard algorithms (bcrypt, scrypt, argon2) to make brute-force and dictionary attacks infeasible.
UNSAFE:
import hashlib
password_hash = hashlib.sha256(password.encode()).hexdigest()
const hash = crypto.createHash('md5').update(password).digest('hex');
MessageDigest md = MessageDigest.getInstance("SHA-512");
byte[] hash = md.digest(password.getBytes());
SAFE:
import bcrypt
hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
from argon2 import PasswordHasher
ph = PasswordHasher()
hashed = ph.hash(password)
const bcrypt = require('bcrypt');
const hashed = await bcrypt.hash(password, 12);
import org.mindrot.jbcrypt.BCrypt;
String hashed = BCrypt.hashpw(password, BCrypt.gensalt(12));
Check 12: Broken/Obsolete Ciphers
CWE-327 (Use of a Broken or Risky Cryptographic Algorithm) | A02:2021 - Cryptographic Failures | Severity: High
WHY: DES (56-bit key) can be brute-forced in hours. 3DES is slow and vulnerable to Sweet32 birthday attacks on 64-bit blocks. RC4 has statistical biases exploitable in TLS (RC4 NOMORE attack). Blowfish has a 64-bit block size vulnerable to birthday attacks after ~32GB of data.
UNSAFE:
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
from Crypto.Cipher import DES
cipher = DES.new(key, DES.MODE_ECB)
const cipher = crypto.createCipheriv('rc4', key, '');
import "crypto/des"
block, _ := des.NewCipher(key)
SAFE:
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
from Crypto.Cipher import AES
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
import "crypto/aes"
import "crypto/cipher"
block, _ := aes.NewCipher(key)
aesgcm, _ := cipher.NewGCM(block)
Findings Format
MANDATORY FORMAT: You MUST include Severity, CWE, and OWASP Top 10:2021 mapping on every finding. You MUST include UNSAFE and SAFE code blocks for each finding.
Each finding should include:
| Field | Description |
|---|
| Severity | Critical / High / Medium / Low |
| CWE | CWE-XXX identifier |
| OWASP | A01-A10 category (OWASP Top 10:2021) |
| Location | file:line |
| Issue | Description of the vulnerability |
| Remediation | How to fix it |
Example Finding
| Field | Value |
|---|
| Severity | Critical |
| CWE | CWE-321 |
| OWASP | A02:2021 - Cryptographic Failures |
| Location | src/encryption.py:14 |
| Issue | AES encryption key hardcoded as string literal b'mysecretkey12345' |
| Remediation | Move key to environment variable or secrets manager; rotate the compromised key immediately |
Reference Tables
Crypto Check to CWE/OWASP Mapping
| # | Check | CWE | OWASP | Default Severity |
|---|
| 1 | Weak hash algorithms (MD5, SHA1) | CWE-328 | A02:2021 - Cryptographic Failures | High |
| 2 | Hardcoded cryptographic keys/IVs | CWE-321 | A02:2021 - Cryptographic Failures | Critical |
| 3 | Insecure random number generation | CWE-330 | A02:2021 - Cryptographic Failures | High |
| 4 | Weak key sizes (<2048-bit RSA) | CWE-326 | A02:2021 - Cryptographic Failures | High |
| 5 | AES in ECB mode | CWE-327 | A02:2021 - Cryptographic Failures | High |
| 6 | Missing HMAC verification | CWE-327 | A02:2021 - Cryptographic Failures | Medium |
| 7 | IV reuse / static IV | CWE-329 | A02:2021 - Cryptographic Failures | High |
| 8 | Improper certificate validation | CWE-295 | A07:2021 - Security Misconfiguration | Critical |
| 9 | Timing attacks (non-constant-time) | CWE-208 | A02:2021 - Cryptographic Failures | Medium |
| 10 | Deprecated TLS versions | CWE-327 | A07:2021 - Security Misconfiguration | High |
| 11 | Weak password hashing | CWE-916 | A02:2021 - Cryptographic Failures | Critical |
| 12 | Broken/obsolete ciphers | CWE-327 | A02:2021 - Cryptographic Failures | High |
OWASP Top 10:2021 Quick Reference
| Category | Description | Related Checks |
|---|
| A02:2021 | Cryptographic Failures | Checks 1-7, 9, 11, 12 |
| A07:2021 | Security Misconfiguration | Checks 8, 10 |
CWE Reference
Example Usage
User prompt:
"Run a crypto audit on this project"
Expected output (abbreviated):
## Crypto Audit Results
Scanned 18 files across JavaScript, Python, Go
### Findings (6 total: 2 Critical, 3 High, 1 Medium)
| # | Severity | CWE | OWASP | Location | Issue |
|---|----------|-----|-------|----------|-------|
| 1 | Critical | CWE-321 | A02 | src/encryption.py:14 | AES key hardcoded as string literal |
| 2 | Critical | CWE-916 | A02 | src/auth/password.js:8 | MD5 used for password hashing |
| 3 | High | CWE-328 | A02 | lib/tokens.py:22 | SHA1 used to generate auth tokens |
| 4 | High | CWE-330 | A02 | src/session.ts:15 | Math.random() used for session ID generation |
| 5 | High | CWE-327 | A02 | pkg/crypto/encrypt.go:31 | AES-ECB mode used for encryption |
| 6 | Medium | CWE-208 | A02 | src/auth/verify.js:44 | HMAC compared with === (timing side-channel) |
### Recommendations
1. Move encryption keys to environment variables or a secrets manager (Finding #1)
2. Replace MD5 password hashing with bcrypt or argon2 (Finding #2)
3. Replace Math.random() with crypto.randomBytes() for all security-sensitive values (Finding #4)