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 with config/hashing.php’s `driver.

  • ASP.NET protects view state and authentication tickets with machineKey’s `validation and decryption attributes.

  • OpenSSH negotiates whatever KexAlgorithms, Ciphers, MACs and HostKeyAlgorithms list.

  • 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.