Node.js crypto 모듈 해시 함수 에러
Understanding and fixing common crypto hash function errors in Node.js including encoding issues and algorithm selection.
Node.js crypto 모듈 해시 함수 에러
Introduction
The Node.js crypto module provides cryptographic functionality including hashing, encryption, and signing. Hash function errors can be subtle and hard to debug because they often produce silent data corruption rather than explicit exceptions.
Environment
node --version
# v20.11.0
# Available algorithms
node -e "console.log(require('crypto').getHashes())"Problem
Hash calculations were producing different results for the same input:
const crypto = require('crypto');
const data = 'Hello, World!';
// These should produce the same hash but don't
const hash1 = crypto.createHash('sha256').update(data).digest('hex');
const hash2 = crypto.createHash('sha256').update(data, 'utf8').digest('hex');
console.log(hash1 === hash2); // false!Analysis
The issue is that update() without encoding specification uses different defaults depending on the input type. The crypto module handles strings, Buffers, and TypedArrays differently.
Common hash errors:
- Encoding mismatch - Different encodings produce different hashes
- Algorithm case sensitivity - 'SHA256' vs 'sha256'
- Streaming updates - Not finalizing the hash correctly
- Binary data handling - Converting strings to bytes incorrectly
Solution
Solution 1: Consistent encoding usage
const crypto = require('crypto');
function createHash(data, algorithm = 'sha256', encoding = 'hex') {
return crypto.createHash(algorithm)
.update(data, 'utf8') // Always specify encoding
.digest(encoding);
}
const data = 'Hello, World!';
console.log(createHash(data));
// 'dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f'Solution 2: Handle binary data
const crypto = require('crypto');
// For binary data
const binaryData = Buffer.from([0x00, 0x01, 0x02, 0x03]);
const hash = crypto.createHash('sha256')
.update(binaryData) // Buffer is handled correctly
.digest('hex');
console.log(hash);
// For hex strings
const hexString = '00010203';
const hash2 = crypto.createHash('sha256')
.update(Buffer.from(hexString, 'hex')) // Convert hex to Buffer first
.digest('hex');Solution 3: Streaming hash for large data
const crypto = require('crypto');
const fs = require('fs');
function hashFile(filePath, algorithm = 'sha256') {
return new Promise((resolve, reject) => {
const hash = crypto.createHash(algorithm);
const stream = fs.createReadStream(filePath);
stream.on('data', (data) => {
hash.update(data);
});
stream.on('end', () => {
resolve(hash.digest('hex'));
});
stream.on('error', reject);
});
}
// Usage
hashFile('large-file.zip').then(console.log);Solution 4: HMAC with proper key handling
const crypto = require('crypto');
function createHMAC(data, key, algorithm = 'sha256') {
// Key must be a Buffer or string
const hmac = crypto.createHmac(algorithm, key);
hmac.update(data, 'utf8');
return hmac.digest('hex');
}
const data = 'Message to sign';
const secretKey = 'my-secret-key';
console.log(createHMAC(data, secretKey));
// Consistent HMAC outputSolution 5: Password hashing with scrypt
const crypto = require('crypto');
const { promisify } = require('util');
const scryptAsync = promisify(crypto.scrypt);
async function hashPassword(password) {
const salt = crypto.randomBytes(16).toString('hex');
const derivedKey = await scryptAsync(password, salt, 64);
return `${salt}:${derivedKey.toString('hex')}`;
}
async function verifyPassword(password, storedHash) {
const [salt, hash] = storedHash.split(':');
const derivedKey = await scryptAsync(password, salt, 64);
return derivedKey.toString('hex') === hash;
}
// Usage
(async () => {
const hash = await hashPassword('my-password');
console.log('Hash:', hash);
const isMatch = await verifyPassword('my-password', hash);
console.log('Match:', isMatch);
})();Lessons Learned
- Always specify encoding in
update()calls - Do not rely on defaults - Use lowercase algorithm names - 'sha256' not 'SHA256'
- Handle binary data as Buffers - Do not convert to strings
- Use streaming for large data - Avoid loading entire files into memory
- Use scrypt or bcrypt for passwords - Never use raw SHA256 for password hashing
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.