Password strength in JavaScript is measured by calculating entropy in bits using the formula length * Math.log2(charsetSize). This metric reflects the actual unpredictability of the password, accounting for both the number of characters and the size of the character set used. A robust checker evaluates these two factors together rather than relying on character count alone.
Why Simple Length Checks Are Insufficient
Basic password validators often fail because they treat length as the sole indicator of security. While length is important, it does not tell the whole story. A password’s strength depends on how many possible characters could occupy each position. An eight-character password drawn from a small set of lowercase letters (26 options) offers far less security than an eight-character password drawn from a full ASCII set (95 options).
A good checker must weigh both the length of the string and the diversity of the character pool. This ensures that a short, complex password is rated appropriately compared to a long, simple one. By calculating entropy, you create a universal metric that allows fair comparison between different password strategies, such as random strings versus multi-word passphrases.
Using the Correct Random Number Generator
When generating passwords to test or use, rely on crypto.getRandomValues() rather than Math.random(). The standard Math.random() function is not cryptographically secure and may produce predictable patterns depending on the browser implementation. crypto.getRandomValues() uses the browser’s native CSPRNG (Cryptographically Secure Pseudorandom Number Generator), which is the same source used for TLS keys.
This distinction matters because predictable randomness lowers the effective entropy of your passwords. When building a strength checker, you are evaluating the output of these generators, so understanding the source of randomness helps in setting realistic expectations for entropy distribution. Using the correct RNG ensures that the passwords you evaluate actually possess the randomness their entropy score claims.
The Entropy Calculation Formula
Entropy is measured in bits and represents the logarithm of the number of possible combinations. The core formula is $log_2(charsetSize^{length})$, which simplifies to $length \times log_2(charsetSize)$. JavaScript’s Math.log2() function handles this calculation efficiently. You first determine the size of the character set (the charset size) and the length of the password. Then, you multiply the length by the base-2 logarithm of the charset size.
This yields a precise bit count that reflects the true unpredictability of the password. This approach avoids arbitrary scoring scales and provides a universal metric that can be compared across different password generation strategies. Higher bit counts indicate stronger passwords because they require more attempts to guess by brute force.
Implementing the Strength Checker
Here is a complete function that calculates entropy and returns a strength score based on bit thresholds. This example uses realistic inputs to demonstrate how different character sets affect the score.
function calculatePasswordStrength(password, charsetSize) {
// Edge case: empty password has 0 entropy
if (!password || password.length === 0) {
return { entropy: 0, score: 'Weak' };
}
// Calculate entropy: length * log2(charsetSize)
const entropy = password.length * Math.log2(charsetSize);
// Determine score based on entropy bits
let score = 'Weak';
if (entropy >= 128) {
score = 'Strong';
} else if (entropy >= 80) {
score = 'Medium';
} else if (entropy >= 60) {
score = 'Fair';
}
return { entropy: Math.round(entropy), score };
}
// Example Usage:
// Password: "A1b2C3d4" (Length: 8)
// Charset: Lowercase + Uppercase + Numbers (26 + 26 + 10 = 62 chars)
const myPassword = "A1b2C3d4";
const charsetSize = 62;
const result = calculatePasswordStrength(myPassword, charsetSize);
console.log(result);
// Output: { entropy: 47, score: 'Weak' }
// Example Usage 2:
// Password: "Tr0ub4dor&3" (Length: 11)
// Charset: Lowercase + Uppercase + Numbers + Symbols (95 chars)
const strongPassword = "Tr0ub4dor&3";
const largeCharsetSize = 95;
const result2 = calculatePasswordStrength(strongPassword, largeCharsetSize);
console.log(result2);
// Output: { entropy: 72, score: 'Fair' }
In the first example, an 8-character password from a 62-character set yields approximately 47 bits of entropy. This is generally considered weak for modern security standards. In the second example, an 11-character password from a 95-character set yields about 72 bits, moving into the "Fair" range. Notice how increasing both length and charset size significantly impacts the score. You can adjust the thresholds (60, 80, 128) based on your specific security requirements, but these are common baselines. For higher security, aiming for 80 bits or more is often recommended. If you need to generate passwords that meet these thresholds automatically, you can use a tool like PasswordForge which shows entropy in bits for every generated option, ensuring you meet your target score without manual calculation.
Handling Diceware and Wordlists
Diceware passphrases operate on a different principle. Instead of a large charset of individual characters, you select words from a fixed list. The entropy calculation remains similar but applies to word choices. If your wordlist has 7,776 words (the standard EFF list), each word adds $log_2(7776) \approx 12.9$ bits of entropy. A four-word passphrase would therefore have approximately $4 \times 12.9 = 51.6$ bits.
This is often comparable to a longer random password but is easier to remember. When checking strength for Diceware phrases, use the wordlist size as the charsetSize parameter in the function above. For example, passing a length of 4 and a charset size of 7776 will correctly calculate the entropy. This method ensures that memorable phrases are not unfairly penalized compared to random strings, as their entropy is derived from the number of possible combinations of words, not just character variety.
Testing Edge Cases and Performance
When implementing this logic, consider edge cases such as empty strings, single-character passwords, or passwords with mixed case and symbols. The function above handles empty strings by returning zero entropy. For performance, Math.log2() is highly optimized in modern JavaScript engines. However, if you are processing thousands of passwords in bulk (for example, during a credential rotation), ensure that the charset size calculation is done once per batch rather than per password if the rules are consistent.
You can cache the Math.log2(charsetSize) value if the charset does not change. This minor optimization prevents redundant logarithm calculations. Always validate that the input password is a string and that the charset size is a positive integer to avoid unexpected behavior. By focusing on accurate entropy calculation rather than arbitrary character counts, you provide a reliable measure of password strength that aligns with cryptographic best practices.