Critical severityNVD Advisory· Published Jun 23, 2025· Updated Apr 15, 2026
CVE-2025-6545
CVE-2025-6545
Description
Improper Input Validation vulnerability in pbkdf2 allows Signature Spoofing by Improper Validation. This vulnerability is associated with program files lib/to-buffer.Js.
This issue affects pbkdf2: from 3.0.10 through 3.1.2.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
pbkdf2npm | >= 3.0.10, < 3.1.3 | 3.1.3 |
Patches
2e3102a8cd483[Refactor] use `to-buffer`
4 files changed · +35 −24
.github/workflows/node-aught.yml+1 −1 modified@@ -6,7 +6,7 @@ jobs: tests: uses: ljharb/actions/.github/workflows/node.yml@main with: - range: '< 10' + range: '>= 0.12 < 10' type: minors command: npm run tests-only
lib/to-buffer.js+13 −9 modified@@ -1,16 +1,20 @@ 'use strict'; var Buffer = require('safe-buffer').Buffer; +var toBuffer = require('to-buffer'); + +var useUint8Array = typeof Uint8Array !== 'undefined'; +var useArrayBuffer = useUint8Array && typeof ArrayBuffer !== 'undefined'; +var isView = useArrayBuffer && ArrayBuffer.isView; module.exports = function (thing, encoding, name) { - if (Buffer.isBuffer(thing)) { - return thing; - } - if (typeof thing === 'string') { - return Buffer.from(thing, encoding); - } - if (ArrayBuffer.isView(thing)) { - return Buffer.from(thing.buffer); + if ( + typeof thing === 'string' + || Buffer.isBuffer(thing) + || (useUint8Array && thing instanceof Uint8Array) + || (isView && isView(thing)) + ) { + return toBuffer(thing, encoding); } - throw new TypeError(name + ' must be a string, a Buffer, a typed array or a DataView'); + throw new TypeError(name + ' must be a string, a Buffer, a Uint8Array, or a DataView'); };
package.json+2 −1 modified@@ -53,7 +53,8 @@ "create-hmac": "^1.1.7", "ripemd160": "=2.0.1", "safe-buffer": "^5.2.1", - "sha.js": "^2.4.11" + "sha.js": "^2.4.11", + "to-buffer": "^1.2.0" }, "engines": { "node": ">=0.12"
test/index.js+19 −13 modified@@ -14,6 +14,8 @@ var fixtures = require('./fixtures'); var js = require('../browser'); var browserImpl = require('../lib/sync-browser'); +var errMsg = function (name) { return new RegExp(name + ' must be a string, a Buffer, a Uint8Array, or a DataView'); }; + var pVersionMajor = parseInt(process.version.split('.')[0].slice(1), 10); /* istanbul ignore next */ if (pVersionMajor !== 4 || process.browser) { @@ -108,25 +110,29 @@ function runTests(name, compat) { tape(name + ' should throw if the password is not a string or an ArrayBuffer', function (t) { t.plan(2); - t['throws'](function () { - compat.pbkdf2(['a'], 'salt', 1, 32, 'sha1', function () {}); - }, /Password must be a string, a Buffer, a typed array or a DataView/); + t['throws']( + function () { compat.pbkdf2(['a'], 'salt', 1, 32, 'sha1', function () {}); }, + errMsg('Password') + ); - t['throws'](function () { - compat.pbkdf2Sync(['a'], 'salt', 1, 32, 'sha1'); - }, /Password must be a string, a Buffer, a typed array or a DataView/); + t['throws']( + function () { compat.pbkdf2Sync(['a'], 'salt', 1, 32, 'sha1'); }, + errMsg('Password') + ); }); tape(name + ' should throw if the salt is not a string or an ArrayBuffer', function (t) { t.plan(2); - t['throws'](function () { - compat.pbkdf2('pass', ['salt'], 1, 32, 'sha1'); - }, /Salt must be a string, a Buffer, a typed array or a DataView/); + t['throws']( + function () { compat.pbkdf2('pass', ['salt'], 1, 32, 'sha1'); }, + errMsg('Salt') + ); - t['throws'](function () { - compat.pbkdf2Sync('pass', ['salt'], 1, 32, 'sha1'); - }, /Salt must be a string, a Buffer, a typed array or a DataView/); + t['throws']( + function () { compat.pbkdf2Sync('pass', ['salt'], 1, 32, 'sha1'); }, + errMsg('Salt') + ); }); var algos = ['sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'ripemd160']; @@ -250,7 +256,7 @@ tape('does not return all zeroes for any algorithm', function (t) { for (var implName in impls) { // eslint-disable-line no-restricted-syntax var pbkdf2Sync = impls[implName]; try { - var key = pbkdf2Sync('secret', 'salt', 100000, 64, algo).toString('hex'); + var key = pbkdf2Sync('secret', 'salt', 1e4, 64, algo).toString('hex'); results[implName] = key; t.doesNotMatch(key, /^0+$/, implName + ' does not return all zeros for ' + algo); } catch (e) {
1 file changed · +45 −3
lib/sync.js+45 −3 modified@@ -1,7 +1,47 @@ -var createHmac = require('create-hmac') +var createHash = require('create-hash') var checkParameters = require('./precondition') var defaultEncoding = require('./default-encoding') var Buffer = require('safe-buffer').Buffer +var ZEROS = Buffer.alloc(128) +var sizes = { + md5: 16, + sha1: 20, + sha224: 28, + sha256: 32, + sha384: 48, + sha512: 64, + rmd160: 20, + ripemd160: 20 +} +function computePad (alg, key) { + var blocksize = (alg === 'sha512' || alg === 'sha384') ? 128 : 64 + if (key.length > blocksize) { + key = hash(alg, key) + } else if (key.length < blocksize) { + key = Buffer.concat([key, ZEROS], blocksize) + } + var ipad = Buffer.allocUnsafe(blocksize) + var opad = Buffer.alloc(blocksize + sizes[alg]) + + for (var i = 0; i < blocksize; i++) { + ipad[i] = key[i] ^ 0x36 + opad[i] = key[i] ^ 0x5C + } + return { + ipad: ipad, + opad: opad, + alg: alg, + blocksize: blocksize + } +} +function hash (algo, buffer) { + return createHash(algo).update(buffer).digest() +} +function hmac (pad, data) { + var h = hash(pad.alg, Buffer.concat([pad.ipad, data])) + h.copy(pad.opad, pad.blocksize) + return hash(pad.alg, pad.opad) +} module.exports = function (password, salt, iterations, keylen, digest) { if (!Buffer.isBuffer(password)) password = Buffer.from(password, defaultEncoding) if (!Buffer.isBuffer(salt)) salt = Buffer.from(salt, defaultEncoding) @@ -10,6 +50,8 @@ module.exports = function (password, salt, iterations, keylen, digest) { digest = digest || 'sha1' + var pad = computePad(digest, password) + var hLen var l = 1 var DK = Buffer.allocUnsafe(keylen) @@ -21,7 +63,7 @@ module.exports = function (password, salt, iterations, keylen, digest) { for (var i = 1; i <= l; i++) { block1.writeUInt32BE(i, salt.length) - var U = createHmac(digest, password).update(block1).digest() + var U = hmac(pad, block1) if (!hLen) { hLen = U.length @@ -33,7 +75,7 @@ module.exports = function (password, salt, iterations, keylen, digest) { U.copy(T, 0, 0, hLen) for (var j = 1; j < iterations; j++) { - U = createHmac(digest, password).update(U).digest() + U = hmac(pad, U) for (var k = 0; k < hLen; k++) T[k] ^= U[k] }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-h7cp-r72f-jxh6ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-6545ghsaADVISORY
- github.com/browserify/pbkdf2/commit/9699045c37a07f8319cfb8d44e2ff4252d7a7078nvdWEB
- github.com/browserify/pbkdf2/commit/e3102a8cd4830a3ac85cd0dd011cc002fdde33bbnvdWEB
- github.com/browserify/pbkdf2/security/advisories/GHSA-h7cp-r72f-jxh6nvdWEB
News mentions
0No linked articles in our index yet.