CWE-347: Improper Verification of Cryptographic Signature
Overview
Improper signature verification occurs when applications accept unsigned data, fail to validate signatures, use weak signature algorithms, or implement flawed verification logic, enabling attackers to forge signatures, tamper with signed data, and bypass authentication.
OWASP Classification
A04:2025 - Cryptographic Failures
Risk
Critical: Flawed signature verification enables JWT forgery (alg=none attack), forged software updates, tampered API requests, bypassed authentication, accepted malicious signed data, and complete compromise of digital signature security guarantees.
Remediation Steps
Core principle: Verify cryptographic signatures correctly (algorithms, cert chains, canonicalization); reject on any verification failure.
Locate the improper signature verification in your code
- Review the flaw details to identify the specific file, line number, and code pattern with flawed signature verification
- Identify the signature verification issue: missing verification, accepting unsigned data, alg=none attack, wrong key, weak algorithm
- Determine what signed data is affected: JWTs, API signatures, software updates, certificates, documents
- Trace the verification flow: where signatures are checked (or not checked) before trusting data
Implement robust signature verification (Primary Defense)
- ALWAYS verify signatures before trusting data: Never skip signature verification; treat unsigned data as untrusted
- Use constant-time comparison for signatures: Prevents timing attacks; use
crypto.timingSafeEqual()or equivalent - Reject if signature verification fails: Don't log and continue; fail immediately if signature is invalid
- Never skip verification in production: Don't use if (process.env.NODE_ENV === 'dev') skipVerify(); always verify
Validate signature algorithm
- Allowlist allowed algorithms: Only accept RSA-SHA256, ECDSA, Ed25519; reject MD5, SHA1, "none"
- Reject "none" algorithm for JWTs: JWT libraries may accept alg=none; explicitly reject it
- Don't allow algorithm switching: Don't let attacker change algorithm (HS256 to RS256 attack)
- Use strong signature schemes: 2048+ bit RSA (prefer 3072+), 256+ bit ECDSA, Ed25519
Proper JWT verification
- Verify signature using correct public key: Use the right key for the issuer; don't use hardcoded or client-provided keys
- Validate alg header (reject "none"): Check algorithm is in allowlist; reject alg=none, alg=HS256 when expecting RS256
- Check exp, iat, nbf claims: Verify token hasn't expired, was issued in past, is not before valid time
- Validate issuer (iss) and audience (aud): Verify token was issued by trusted issuer and intended for your application
- Use established libraries (NOT custom crypto): Use jsonwebtoken (Node.js), PyJWT (Python), jose4j (Java); don't implement JWT yourself
Certificate/key validation
- Verify certificate chain to trusted CA: Check the entire chain from leaf to root CA
- Check certificate expiration: Verify certificate is currently valid (notBefore <= now <= notAfter)
- Validate certificate hostname: Verify certificate CN/SAN matches the hostname being connected to
- Use proper public key for verification: Ensure you're using the correct public key for the signer
Test the signature verification fix
- Verify unsigned data is rejected (send JWT without signature, should fail)
- Test with invalid signatures: Tamper with signature or signed data, verify rejection
- Test alg=none attack: Send JWT with alg=none, verify it's rejected
- Test algorithm confusion: Try switching algorithm (HS256 vs RS256), verify rejection
- Test with valid signatures: Ensure legitimate signed data is accepted
- Re-scan with security scanner to confirm the issue is resolved
Common Vulnerable Patterns
- Accepting JWT with alg=none
- Not verifying signature at all
- Using wrong public key
- Time-of-check/time-of-use issues
- Custom signature verification code