The npm crypto-js library is a pure-JavaScript implementation of common crypto primitives, and while it is heavily depended on, it carries a real cryptographic weakness (CVE-2023-46233) in versions before 4.2.0 that makes its default PBKDF2 configuration dangerously weak for password hashing. If you use crypto-js, the first thing to verify is your installed version and how you call its key-derivation functions. This review covers the known issue, safe configuration, and when a different tool is the better answer.
crypto-js has been around since the early days of browser JavaScript, when there was no native crypto API. It provides hashing (MD5, SHA family), HMAC, AES, and PBKDF2 in a form that runs anywhere JavaScript does. That portability is why it still shows up in so many package.json files.
The CVE-2023-46233 weakness
The headline issue with crypto-js npm is CVE-2023-46233, disclosed in October 2023. Prior to version 4.2.0, the library's PBKDF2 implementation defaulted to SHA-1 as the underlying hash and to a single iteration. PBKDF2 is a key-derivation function whose whole point is to be slow: you raise the iteration count so that brute-forcing a stolen hash becomes computationally expensive.
The maintainers' own advisory describes the impact bluntly: the default was roughly 1,000 times weaker than what was recommended back in 1993, and over a million times weaker than a modern standard. If you called CryptoJS.PBKDF2(password, salt) without explicit options to hash and store passwords, an attacker who obtained the hashes could crack them at high speed.
The fix landed in 4.2.0, which raises the defaults. If you must stay on an older release, the documented workaround is to configure PBKDF2 explicitly:
// Explicit, safe configuration for crypto-js PBKDF2
const key = CryptoJS.PBKDF2(password, salt, {
keySize: 256 / 32,
iterations: 250000,
hasher: CryptoJS.algo.SHA256,
});
Check what you have installed and upgrade:
npm ls crypto-js
npm install crypto-js@latest # 4.2.0 or newer
Confirm the CVE detail yourself against the GitHub advisory GHSA-xwcq-pm8m-c4vf rather than trusting any single summary, this article included.
Common misuse patterns
Even a patched crypto js npm build can be misused. A few patterns come up repeatedly in reviews:
- Hashing passwords with a bare digest.
CryptoJS.SHA256(password)is not password storage. A single hash, no matter the algorithm, is trivially brute-forced without a slow KDF and a per-user salt. Use PBKDF2 with a high iteration count, or better, a memory-hard function. - MD5 and SHA-1 for anything security-relevant. Both are present in crypto-js for compatibility. They are fine for non-security checksums and broken for integrity or signature use.
- ECB-mode AES. The default cipher mode matters. ECB leaks structure in the plaintext; use an authenticated mode and never reuse an IV.
- Homegrown "encryption" of secrets in the browser. Anything the client can decrypt, an attacker with the client can decrypt too.
None of these are bugs in the library. They are usage decisions, and they are exactly the kind of thing a code review or a linting rule should flag.
When to use the Web Crypto API instead
For most modern environments, you may not need crypto-js at all. Browsers and Node.js both ship the Web Crypto API (crypto.subtle), a native implementation that is faster, harder to misuse, and does not add a dependency to your bundle.
// PBKDF2 via native Web Crypto (browser or Node 15+)
const keyMaterial = await crypto.subtle.importKey(
"raw", new TextEncoder().encode(password), "PBKDF2", false, ["deriveBits"]
);
const bits = await crypto.subtle.deriveBits(
{ name: "PBKDF2", salt, iterations: 250000, hash: "SHA-256" },
keyMaterial, 256
);
crypto-js still earns its place where you need synchronous APIs, must support very old runtimes, or depend on a library that pulls it in transitively. For greenfield code targeting current platforms, native crypto is the safer default. And for password hashing specifically, prefer a memory-hard algorithm such as argon2 or bcrypt over any PBKDF2 configuration.
Managing the dependency over time
The npm crypto-js situation is a good example of why version drift is a security problem, not just a maintenance chore. A dependency that was fine when you added it can become a liability the moment a CVE is published against your pinned version. Two habits keep you ahead of that:
Pin and audit. Commit your lockfile and run npm audit in CI so a newly disclosed advisory against crypto-js surfaces on the next build rather than months later. Because crypto-js is frequently a transitive dependency, an SCA tool is worth adding to catch cases where you never required it directly but ship it anyway. A scanner such as Safeguard can trace that transitive path and tell you which of your direct dependencies dragged it in.
Watch for the "installed but unused" case too. Bundlers sometimes retain crypto-js even after you have migrated to native crypto, so verify with npm ls crypto-js after refactoring. If you want a deeper primer on reading these findings, the Safeguard Academy covers dependency triage.
FAQ
Is crypto-js safe to use in 2025?
Yes, on version 4.2.0 or later and with correct configuration. Upgrade off any release before 4.2.0 because of the weak PBKDF2 defaults in CVE-2023-46233, and always set iterations and hasher explicitly for key derivation.
Should I use crypto-js or the built-in Web Crypto API?
For modern browsers and Node.js, prefer the native Web Crypto API. It is faster, adds no dependency, and is harder to misconfigure. Reach for crypto-js when you need synchronous calls or must support legacy runtimes.
Can I hash passwords with crypto-js SHA256?
No. A bare digest is not password storage. Use a slow, salted key-derivation function, and for new systems prefer a memory-hard algorithm like argon2 or bcrypt over PBKDF2.
How do I know if crypto-js is even in my project?
Run npm ls crypto-js to see whether it is installed and which package required it. It frequently arrives as a transitive dependency, so it can be present even when it is not in your own package.json.