Bcrypt Password Hash Generator
Generate secure bcrypt password hashes with customizable salt rounds
Bcrypt Password Hash Generator
Generate secure bcrypt password hashes with customizable salt rounds
Generate Bcrypt Hash
Verify Password
π Bcrypt Security Tips
- β’ Salt Rounds: Use 10-12 rounds for most applications, 12-15 for high-security
- β’ Performance: Higher rounds = more secure but slower (test on your hardware)
- β’ Future-Proof: You can increase rounds over time as hardware improves
- β’ Never Store: Never store plain text passwords, always hash them
- β’ Unique Salts: Bcrypt automatically generates unique salts for each hash
About Bcrypt Password Hashing
Bcrypt is a password hashing function designed by Niels Provos and David Mazières, based on the Blowfish cipher. It's specifically designed to be slow and computationally expensive, making it resistant to brute-force attacks and ideal for secure password storage.
- Adaptive hashing function with configurable work factor
- Built-in salt generation for each password
- Time-tested and widely adopted in production systems
- Resistant to rainbow table and brute-force attacks
- Future-proof with adjustable computational cost
Bcrypt Security Features
Security Benefits
- Adaptive Cost: Adjustable work factor (salt rounds)
- Built-in Salt: Automatic unique salt for each hash
- Time Delay: Intentionally slow to prevent brute force
- Future Proof: Increase cost as hardware improves
- Battle Tested: Used by major platforms worldwide
Technical Aspects
- Algorithm: Based on Blowfish encryption
- Output Length: 60 character hash string
- Salt Rounds: 4-31 (recommended: 10-12)
- Format: $2a$rounds$salt+hash
- Verification: Compare plaintext to stored hash
Advertisement
Frequently Asked Questions
What are salt rounds and how many should I use?
Salt rounds determine how computationally expensive the hashing process is. Each round doubles the time required. Use 10-12 rounds for most applications. Higher rounds (13-15) for high-security scenarios, but test performance impact.
Is bcrypt better than SHA-256 for passwords?
Yes! SHA-256 is fast by design, making it vulnerable to brute-force attacks. Bcrypt is intentionally slow and includes built-in salting, making it much more secure for password storage. Never use fast hashes like SHA-256 for passwords.
Can I increase salt rounds for existing passwords?
You can't modify existing hashes, but you can implement a migration strategy: check if a password uses old rounds during login, and if so, rehash with new rounds after successful verification.
How long should bcrypt hashing take?
Aim for 250-500ms on your server hardware. This provides good security while maintaining acceptable user experience. Adjust salt rounds based on your server's performance and security requirements.
Bcrypt Hash Examples
Hash Structure:
Salt Rounds Comparison:
Sponsored Content
Bcrypt Implementation
JavaScript Bcrypt Implementation:
class BcryptGenerator {
constructor() {
this.defaultRounds = 12;
this.minRounds = 4;
this.maxRounds = 31;
// Bcrypt character set for encoding
this.charset = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
}
// Generate bcrypt hash
async generateHash(password, saltRounds = this.defaultRounds) {
try {
if (typeof password !== 'string' || password.length === 0) {
return {
success: false,
error: 'Password must be a non-empty string'
};
}
if (!this.isValidSaltRounds(saltRounds)) {
return {
success: false,
error: `Salt rounds must be between ${this.minRounds} and ${this.maxRounds}`
};
}
const startTime = performance.now();
// Generate salt
const salt = this.generateSalt(saltRounds);
// Hash password with salt
const hash = await this.hashPassword(password, salt);
const endTime = performance.now();
const duration = endTime - startTime;
return {
success: true,
hash: hash,
salt: salt,
rounds: saltRounds,
timing: {
duration: Math.round(duration),
formatted: `${Math.round(duration)}ms`
},
analysis: this.analyzeHash(hash),
security: this.getSecurityAnalysis(saltRounds, duration)
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
// Verify password against hash
async verifyPassword(password, hash) {
try {
if (!this.isValidHash(hash)) {
return {
success: false,
error: 'Invalid bcrypt hash format'
};
}
const startTime = performance.now();
// Extract salt from hash
const salt = this.extractSalt(hash);
// Hash the provided password with extracted salt
const testHash = await this.hashPassword(password, salt);
const endTime = performance.now();
const duration = endTime - startTime;
const matches = this.constantTimeCompare(hash, testHash);
return {
success: true,
matches: matches,
timing: {
duration: Math.round(duration),
formatted: `${Math.round(duration)}ms`
},
hashInfo: this.analyzeHash(hash)
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
// Generate bcrypt salt
generateSalt(rounds) {
const roundsFormatted = rounds.toString().padStart(2, '0');
const saltBytes = this.generateRandomBytes(16);
const encodedSalt = this.base64Encode(saltBytes);
return `$2a$${roundsFormatted}$${encodedSalt}`;
}
// Hash password with salt (simplified implementation)
async hashPassword(password, salt) {
// Note: This is a simplified demonstration
// In production, use a proper bcrypt library like bcryptjs
const rounds = this.extractRounds(salt);
const saltOnly = this.extractSaltOnly(salt);
// Simulate bcrypt's expensive key derivation
let hash = password + saltOnly;
// Simulate the iterative process
for (let i = 0; i < Math.pow(2, rounds); i++) {
hash = await this.simpleHash(hash);
}
const hashBytes = this.stringToBytes(hash.substring(0, 23));
const encodedHash = this.base64Encode(hashBytes);
return salt + encodedHash;
}
// Simple hash function for simulation
async simpleHash(input) {
const encoder = new TextEncoder();
const data = encoder.encode(input);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
// Generate cryptographically secure random bytes
generateRandomBytes(length) {
const bytes = new Uint8Array(length);
crypto.getRandomValues(bytes);
return bytes;
}
// Encode bytes using bcrypt's modified base64
base64Encode(bytes) {
let result = '';
for (let i = 0; i < bytes.length; i += 3) {
const b1 = bytes[i] || 0;
const b2 = bytes[i + 1] || 0;
const b3 = bytes[i + 2] || 0;
const combined = (b1 << 16) | (b2 << 8) | b3;
result += this.charset[(combined >>> 18) & 0x3F];
result += this.charset[(combined >>> 12) & 0x3F];
if (i + 1 < bytes.length) result += this.charset[(combined >>> 6) & 0x3F];
if (i + 2 < bytes.length) result += this.charset[combined & 0x3F];
}
return result.substring(0, 22); // Bcrypt salt is 22 chars
}
// Convert string to byte array
stringToBytes(str) {
const bytes = new Uint8Array(str.length);
for (let i = 0; i < str.length; i++) {
bytes[i] = str.charCodeAt(i) & 0xFF;
}
return bytes;
}
// Validate salt rounds
isValidSaltRounds(rounds) {
return Number.isInteger(rounds) &&
rounds >= this.minRounds &&
rounds <= this.maxRounds;
}
// Validate bcrypt hash format
isValidHash(hash) {
const bcryptRegex = /^\$2[abxy]?\$\d{2}\$[./A-Za-z0-9]{53}$/;
return bcryptRegex.test(hash);
}
// Extract salt from hash
extractSalt(hash) {
const parts = hash.split('$');
if (parts.length >= 4) {
return `$${parts[1]}$${parts[2]}$${parts[3]}`;
}
throw new Error('Invalid hash format');
}
// Extract rounds from salt
extractRounds(salt) {
const parts = salt.split('$');
return parseInt(parts[2], 10);
}
// Extract salt-only part
extractSaltOnly(salt) {
const parts = salt.split('$');
return parts[3];
}
// Constant time string comparison
constantTimeCompare(a, b) {
if (a.length !== b.length) return false;
let result = 0;
for (let i = 0; i < a.length; i++) {
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
}
return result === 0;
}
// Analyze hash structure
analyzeHash(hash) {
try {
const parts = hash.split('$');
return {
algorithm: parts[1],
rounds: parseInt(parts[2], 10),
salt: parts[3],
hash: parts[4],
totalLength: hash.length,
format: 'bcrypt',
isValid: this.isValidHash(hash)
};
} catch (error) {
return { error: 'Invalid hash format' };
}
}
// Security analysis based on rounds and timing
getSecurityAnalysis(rounds, duration) {
const analysis = {
rounds: rounds,
duration: duration,
strength: 'Unknown',
recommendation: '',
futureProof: false
};
if (rounds < 10) {
analysis.strength = 'Weak';
analysis.recommendation = 'Increase to at least 10 rounds';
} else if (rounds < 12) {
analysis.strength = 'Good';
analysis.recommendation = 'Consider increasing to 12 rounds';
} else if (rounds < 15) {
analysis.strength = 'Strong';
analysis.recommendation = 'Excellent security level';
analysis.futureProof = true;
} else {
analysis.strength = 'Very Strong';
analysis.recommendation = 'Maximum security (may impact performance)';
analysis.futureProof = true;
}
// Performance analysis
if (duration > 1000) {
analysis.performanceWarning = 'Hash time > 1s may impact user experience';
} else if (duration < 100) {
analysis.performanceWarning = 'Hash time < 100ms may not be secure enough';
}
return analysis;
}
// Generate multiple hashes for comparison
async generateBatch(passwords, rounds = this.defaultRounds) {
const results = [];
for (const password of passwords) {
const result = await this.generateHash(password, rounds);
results.push({
password: password,
result: result
});
}
return {
success: true,
count: passwords.length,
rounds: rounds,
results: results,
totalTime: results.reduce((sum, r) =>
sum + (r.result.timing?.duration || 0), 0)
};
}
// Compare different salt rounds performance
async benchmarkRounds(password, roundsArray = [8, 10, 12, 14]) {
const benchmarks = [];
for (const rounds of roundsArray) {
const result = await this.generateHash(password, rounds);
if (result.success) {
benchmarks.push({
rounds: rounds,
duration: result.timing.duration,
hash: result.hash,
security: result.security.strength
});
}
}
return {
success: true,
password: '***hidden***',
benchmarks: benchmarks,
recommendation: this.getOptimalRounds(benchmarks)
};
}
// Get optimal rounds recommendation
getOptimalRounds(benchmarks) {
const target = 250; // Target 250ms
let bestRounds = 12;
let bestDiff = Infinity;
for (const bench of benchmarks) {
const diff = Math.abs(bench.duration - target);
if (diff < bestDiff) {
bestDiff = diff;
bestRounds = bench.rounds;
}
}
return {
rounds: bestRounds,
reason: `Closest to ${target}ms target duration`,
actualDuration: benchmarks.find(b => b.rounds === bestRounds)?.duration
};
}
// Migrate to higher rounds
async migrateHash(password, oldHash, newRounds) {
// First verify the password with old hash
const verification = await this.verifyPassword(password, oldHash);
if (!verification.success || !verification.matches) {
return {
success: false,
error: 'Password does not match existing hash'
};
}
// Generate new hash with higher rounds
const newHash = await this.generateHash(password, newRounds);
if (newHash.success) {
return {
success: true,
oldHash: oldHash,
newHash: newHash.hash,
oldRounds: verification.hashInfo.rounds,
newRounds: newRounds,
improvement: `Security increased from ${verification.hashInfo.rounds} to ${newRounds} rounds`
};
}
return newHash;
}
}
// Usage examples
const bcrypt = new BcryptGenerator();
// Generate hash
console.log('Generate Bcrypt Hash:');
const password = 'mySecurePassword123!';
const hashResult = await bcrypt.generateHash(password, 12);
console.log(hashResult);
// Verify password
console.log('\nVerify Password:');
if (hashResult.success) {
const verifyResult = await bcrypt.verifyPassword(password, hashResult.hash);
console.log(verifyResult);
}
// Benchmark different rounds
console.log('\nBenchmark Salt Rounds:');
const benchmark = await bcrypt.benchmarkRounds('testPassword');
console.log(benchmark);
console.log('Bcrypt generator ready!');
Bcrypt Best Practices
- Choose Appropriate Rounds: Start with 12 rounds, adjust based on performance needs
- Monitor Performance: Hash time should be 250-500ms on your hardware
- Plan for Migration: Implement system to upgrade rounds over time
- Use Proper Libraries: Use established bcrypt libraries, not custom implementations
- Secure Password Input: Always validate and sanitize password input
- Constant Time Comparison: Use timing-safe comparison for verification
- Store Safely: Protect hash storage with database security measures
Common Use Cases
- User authentication systems
- Web application login security
- API authentication backends
- Password management systems
- Multi-factor authentication setup
- Enterprise security applications
- Mobile app user accounts
- Database security implementations
Advertisement
