Broken or deprecated algorithm chosen by configuration
ID |
weak_crypto_algorithm_in_config |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
medium |
Remediation Effort |
low |
Family |
Cryptography |
Tags |
ASVS50:v11.2.1, ASVS50:v11.4.1, ASVS50:v11.6.1, CWE:327, configuration, cryptography, non-reachable, security, source-code |
Description
A large share of an application’s cryptography is never named in code. The algorithm is declared in configuration and read at run time by the framework:
-
Jasypt encrypts Spring properties with
jasypt.encryptor.algorithm. -
Django hashes every new password with the first entry of
PASSWORD_HASHERS. -
Laravel encrypts with
config/app.php’s `cipher, and hashes withconfig/hashing.php’s `driver. -
ASP.NET protects view state and authentication tickets with
machineKey’s `validationanddecryptionattributes. -
OpenSSH negotiates whatever
KexAlgorithms,Ciphers,MACsandHostKeyAlgorithmslist. -
A Keycloak realm signs its tokens with
defaultSignatureAlgorithm.
Because no call site names these algorithms, a code review and a SAST scan both pass over them. This detector reads the shipped configuration-key catalogue — the same catalogue the CBOM collector uses to inventory configuration-declared cryptography — and reports a value the algorithm catalogue classifies as broken or deprecated.
Risky-but-current algorithms are deliberately not reported. AES-CBC is risky in the abstract and is also Laravel’s shipped default; a rule that fires on every stock installation is a rule nobody reads. A value the catalogue does not recognise is not reported either — nothing is guessed.
Security
The algorithm named here is the algorithm the application actually uses, for every value it protects.
PBEWithMD5AndDES, Jasypt’s historical default, is a PBES1 construction over MD5 and single DES: the
56-bit key is exhaustible and the key derivation is a single MD5 pass, so an attacker who obtains the
encrypted properties recovers them offline. An MD5PasswordHasher first in PASSWORD_HASHERS means
every account created since that line landed has an unsalted-grade MD5 password hash, recoverable from
a rainbow table.
The configuration form makes the exposure worse in two ways. It applies process-wide rather than at one call site, so a single line covers everything the framework protects; and it is invisible to the review practices aimed at code, so it survives long after the equivalent hard-coded call would have been caught.
Mitigation / Fix
Change the configured algorithm to a current one, then deal with the data already protected by the old one:
# Spring / Jasypt — PBES2 with HMAC-SHA-512 and AES-256
jasypt.encryptor.algorithm=PBEWITHHMACSHA512ANDAES_256
jasypt.encryptor.ivGeneratorClassname=org.jasypt.iv.RandomIvGenerator
# Django — the first entry hashes new passwords; keep legacy entries only to verify old ones
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
]
Rotation is the part that is easy to forget. Changing the setting protects new values only:
-
Encrypted data has to be re-encrypted with the new algorithm.
-
Password hashes upgrade on next successful login when the old hasher is still listed for verification — so keep it listed, and drop it once the rehash campaign is complete.
-
Signing keys used with a broken digest must be treated as compromised and replaced.
Run xygeni inventory --cbom --cbom-file cbom.json to see the configuration-declared algorithms as
first-class assets in the CBOM, and --crypto-coverage for what the app-config channel does and
does not read.