Password column stored in plain text
A login password column is written with the raw value. If the database leaks, every password is readable as-is, and users who reuse passwords are exposed elsewhere too.
What Vulkro detects
Rule schema-plaintext-password-column, default severity High, risk category Data protection.
Vulkro finds the password column in the database schema (a model or migration), then every place the code assigns it in a create, update or save call. The rule fires when none of those write sites hashes the value: not the write itself, not the value's own assignment, and not a pre-save hook or setter in the model's file. Test, seed, fixture and mock files are ignored. The finding sits on the write site and names the line where the column is declared.
Non-compliant code
// controllers/auth.js: the request password is stored as-is
exports.register = async (req, res) => {
const user = await User.create({
email: req.body.email,
password: req.body.password,
});
res.status(201).json({ id: user.id });
};
Compliant code
// controllers/auth.js: hashed with bcrypt before it is stored
exports.register = async (req, res) => {
const passwordHash = await bcrypt.hash(req.body.password, 12);
const user = await User.create({
email: req.body.email,
password: passwordHash,
});
res.status(201).json({ id: user.id });
};
How to fix
- Hash with a slow, salted password hash before storing: bcrypt (cost 12 or more), argon2id or scrypt.
- Do it in one place the model owns (a
pre('save')hook, a setter,set_password,Hash::make, aPasswordEncoder) so no write path can skip it. - Verify with the library's constant-time compare on login.
- Force a reset for any account whose stored value is not a hash.
References
See also: Database schema discovery, API2 Broken Authentication.