Monday 30 September 2019

Senior Devs: Looking for feedback on basic password hashing strategy

I'm implementing basic password hashing/validation using Node/Express/Bookshelf/Postgres. crypto module for encryption. I'd appreciate critique/feedback // protocol.config.js const protocols = { 'v1': { keylen: 64, digest: 'sha512', iterations: 100000, encoding: 'hex', salt: { size: 128, encoding: 'base64' }, }, } // UserModel.js class User extends bookshelf.Model { get password(){ return this.attributes.password; } get salt(){ return this.attributes.salt; } get protocol(){ return 'v1' } static async generateSalt(config){ const buf = await randomBytes(config.size); return buf.toString(config.encoding); } static async encrypt(string, salt, config){ const derivedKey = await pbkdf2(string, salt, config.iterations, config.keylen, config.digest); return derivedKey.toString(config.encoding); } static async save({ password, ...data }){ const salt = await User.generateSalt(protocols[this.protocol].salt); const hash = await User.encrypt(password, salt, protocols[this.protocol]); const hashSaltProtocol = String.prototype.concat.call(hash, '$', salt, '$', this.protocol); return this.forge({ ...data, password: hashSaltProtocol }).save(); } async comparePassword(string){ const [password, salt, protocol] = this.password.split('$'); const hash = await User.encrypt(string, salt, protocols[protocol]); return crypto.timingSafeEqual( Buffer.from(password, protocols[protocol].encoding), Buffer.from(hash, protocols[protocol].encoding) ); } } High level explanation:Save all password protocols in external file.Set current password protocol on the model.Password is hashed using current protocol.Random salt is generated using current protocol.Password, salt and version are saved together in password field in User table. ie "passwordhash$salt$version"When user submits a password for validation, the saved password is retrieved from table and split.Submitted password is encrypted using salt and version of the saved password.Password and submitted password are compared using timingSafeEqual instead of naive comparison (i.e. "===");The big idea is that I want a flexible solution for bumping password hashing protocols without impacting passwords hashed from previous versions or storing that data in the table.

Submitted September 30, 2019 at 02:56PM by brodega

No comments:

Post a Comment