npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

phpass

v1.0.0

Published

Verify phpass portable hashes and bcrypt passwords in Node.js, with an explicit migration path for legacy node-phpass hashes.

Readme

phpass

CI npm

Bring a legacy PHP password database into Node.js. phpass verifies portable $P$ / $H$ hashes and standard bcrypt hashes, and generates new bcrypt hashes through bcrypt.js. It includes synchronous and Promise APIs, TypeScript declarations, and a separate migration path for passwords created by the original node-phpass implementation.

Its purpose is interoperability with existing databases. Portable hashes are verified for migration; new hashes use bcrypt. This package does not implement PHP's extended DES fallback, Drupal $S$ hashes, or application-specific wrappers.

Install

npm install phpass

Version 1 requires Node.js 22 or newer. Its runtime uses JavaScript and Node's built-in crypto API; consumers do not need a native compiler.

Hash and verify

Save as example.mjs and run node example.mjs:

import { PasswordHash } from 'phpass';

const passwords = new PasswordHash(10);
const storedHash = await passwords.hashPasswordAsync('correct horse battery staple');

console.log(await passwords.checkPasswordAsync('correct horse battery staple', storedHash));
// true
console.log(await passwords.checkPasswordAsync('wrong password', storedHash));
// false

For CommonJS use const { PasswordHash } = require('phpass'). The original synchronous API remains available as hashPassword() and checkPassword().

Supported formats

| Stored hash | Verification | New hash generation | | --- | --- | --- | | bcrypt $2a$, $2b$, $2y$ | checkPassword() / checkPasswordAsync() | bcrypt $2b$ | | phpass portable $P$ | Same methods | Unsupported | | phpBB portable $H$ | Same methods | Unsupported | | Non-ASCII hashes created by node-phpass 0.1.x | Explicit checkPasswordLegacy() | Unsupported |

Portable verification implements the public-domain Openwall phpass algorithm. The tests include outputs from Openwall's independent C implementation, covering empty, ASCII, UTF-8, and long passwords at multiple iteration counts.

Migrate a portable hash

Verify the supplied password against the stored hash. After successful verification, create a new hash from that same password and persist it through your application's normal account update:

import { PasswordHash } from 'phpass';

const passwords = new PasswordHash(10);
// A public test fixture for the password "test", not a real account credential.
const oldHash = '$P$5123456782Jd0mCwOvdg2EsRtmpU9H1';

if (await passwords.checkPasswordAsync('test', oldHash)) {
  const replacement = await passwords.hashPasswordAsync('test');
  console.log(replacement); // Store this in place of oldHash.
}

Passwords over 72 UTF-8 bytes can be verified against portable hashes, but cannot be passed to hashPassword(): choose an explicit account migration/reset policy for those records rather than silently truncating their passwords.

API and limits

const passwords = new PasswordHash(10, false, {
  maxBcryptCost: 16,
  maxPortableCost: 20,
});

The first argument is the bcrypt generation cost (default 10). Verification reads the cost from the stored hash, independently of that setting. The second argument must remain false: portable hash generation is unsupported. The optional third argument bounds verification work before hashing begins.

| Method | Returns | | --- | --- | | hashPassword(password) | A bcrypt hash; blocks until complete | | hashPasswordAsync(password) | Promise<string>; bcrypt work yields between chunks | | checkPassword(password, storedHash) | Whether a supported hash matches; blocks until complete | | checkPasswordAsync(password, storedHash) | Promise<boolean>; portable work uses a worker thread | | checkPasswordLegacy(password, storedHash) | Whether an explicitly identified node-phpass 0.1.x hash matches |

Passwords are strings encoded as UTF-8. New bcrypt passwords may contain at most 72 bytes. Verification accepts up to 4,096 UTF-8 bytes; bcrypt retains its historical 72-byte truncation behavior when checking an existing hash. Legacy verification instead limits the old representation to 4,096 UTF-16 code units.

Malformed, unsupported, and over-policy hashes return false. Non-string passwords and invalid configuration throw (async methods reject). Hashing errors, such as an unavailable MD5 implementation in a restricted crypto runtime, also propagate. Generation cost must be an integer from 4 through maxBcryptCost; verification ceilings allow 4–31 for bcrypt and 7–30 for portable hashes. Each cost increment doubles the work, so choose ceilings for your stored database and workload before increasing them.

Async methods do not impose a concurrency limit. Bound concurrent verification in your application, particularly portable verification, which starts one worker per call. Sync methods run on the calling thread. None of these methods handles account storage, login throttling, or session management.

Upgrading from 0.1.x

Version 1 deliberately changes several behaviors:

  • New salts use cryptographic randomness; the bundled Math.random() salt path is replaced. The default generation cost increases from 8 to 10.
  • Verification uses each stored hash's cost. You no longer need a separate PasswordHash instance matching every record's original cost.
  • New passwords use standard UTF-8 bcrypt encoding and reject inputs beyond 72 bytes. Generated hashes use $2b$.
  • Malformed/unsupported hashes return false, configuration is validated, and verification costs are bounded. TypeScript declarations are included.

Identify records created by node-phpass 0.1.x before migrating non-ASCII passwords. That release reduced UTF-16 code units to individual low bytes, which is not UTF-8 and can map different passwords to the same byte sequence. For those records only, use checkPasswordLegacy(), then replace the hash after a successful check. It is synchronous and retains the old encoding behavior solely for migration. The normal verification methods never fall back to it automatically. The $2a$ prefix alone does not identify a node-phpass record.

See CHANGELOG.md for release details.

Development

npm ci
npm test
npm run test:types
npm run test:coverage
npm pack --dry-run

CI runs on Node.js 22, 24, and 26. Tests cross-check standard hashes with native bcrypt, portable hashes with Openwall-generated fixtures, and legacy hashes with fixtures generated by node-phpass 0.1.1. Native bcrypt is a development-only oracle and is not installed by package consumers.

License and credits

MIT, with original code copyright © 2011 Cull TV, Inc. The legacy bcrypt implementation was originally credited to jsBCrypt under the New BSD license. Portable verification follows Solar Designer's public-domain algorithm; standard bcrypt is provided by bcrypt.js. See THIRD_PARTY.md.