Secure Hashing of Passwords in Workers

Working on a project where I want to salt and hash passwords using cloudflare workers, I'm using next-on-pages with Nextjs 14 and this means that I can't access libraries like bcrypt or similar that require os or fs. I have a hacky solution but it's just that, a hacky workaround and while security isn't something I'm fussed about, I would at least like to use a validated salt and hash strategy. If anyone knows any libraries that would work or other solutions please let me know. Here is my hacky solution, this was botched together in a single afternoon so I'm not expecting it to work well but it does the job for now:
static async hashPassword(
password: string,
salt?: string
): Promise<HashedCredentials> {
let saltArray = null;

if (salt) {
saltArray = new TextEncoder().encode(salt);
} else {
saltArray = crypto.getRandomValues(new Uint8Array(16));
}

const saltBuffer = new TextEncoder().encode(saltArray);
const passwordBuffer = new TextEncoder().encode(password);

// Concatenate the password and salt
const concatenatedBuffer = new Uint8Array(
passwordBuffer.byteLength + saltBuffer.byteLength
);
concatenatedBuffer.set(new Uint8Array(passwordBuffer), 0);
concatenatedBuffer.set(
new Uint8Array(saltBuffer),
passwordBuffer.byteLength
);

// Hash the concatenated password and salt
const hashBuffer = await crypto.subtle.digest(
"SHA-256",
concatenatedBuffer
);

// Convert the hash and salt to hexadecimal strings
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("");
const saltHex = saltArray
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("");

return { passwordHash: hashHex, passwordSalt: saltHex };
}
static async hashPassword(
password: string,
salt?: string
): Promise<HashedCredentials> {
let saltArray = null;

if (salt) {
saltArray = new TextEncoder().encode(salt);
} else {
saltArray = crypto.getRandomValues(new Uint8Array(16));
}

const saltBuffer = new TextEncoder().encode(saltArray);
const passwordBuffer = new TextEncoder().encode(password);

// Concatenate the password and salt
const concatenatedBuffer = new Uint8Array(
passwordBuffer.byteLength + saltBuffer.byteLength
);
concatenatedBuffer.set(new Uint8Array(passwordBuffer), 0);
concatenatedBuffer.set(
new Uint8Array(saltBuffer),
passwordBuffer.byteLength
);

// Hash the concatenated password and salt
const hashBuffer = await crypto.subtle.digest(
"SHA-256",
concatenatedBuffer
);

// Convert the hash and salt to hexadecimal strings
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("");
const saltHex = saltArray
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("");

return { passwordHash: hashHex, passwordSalt: saltHex };
}
2 Replies
Canopy
CanopyOP9mo ago
I'm so stupid, I must have just completely blanked that module's existence while franticly searching for ages. Thank you very much @Leo ! I did so much unnecessary work lol.
Armando de la Vega
bcrypt-js is not working for me (NextJS 14) using Edge. How did you fix it?
Want results from more Discord servers?
Add your server