T09 · Insecure Skill Coding Practices
- Location
SKILL.md:217- Finding
Insecure Password Verification and Sensitive User Record Disclosure
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 217–224
Vulnerability Type: Insecure authentication and excessive exposure of sensitive account data
Risk Level: MediumVulnerable Code
javascript var user = db.selectOne("select * from user where username = ?", body.username); if (!user || user.password != body.password) { return {code: 401, msg: "Username or password is incorrect"}; } // ... var token = generateToken(user.id); cache.set("token:" + token, user.id, 86400); return {code: 200, data: {token: token, user: user}};Technical Analysis
The example compares the submitted password directly with the value stored in
user.password. This pattern encourages plaintext or reversibly encrypted password storage instead of verification through a dedicated password-hashing function. Although a later recommendation advises encrypted storage, it does not correct this directly reusable example and also identifies salted MD5 as an option, which is not suitable for modern password storage.The login response also returns the complete database-derived
userobject. Because the query selects all columns, this object may contain a plaintext password, password hash, recovery information, authorization flags, internal identifiers, or other fields that should never be returned to a client.The token-generation operation is not defined sufficiently to establish whether tokens are generated using a cryptographically secure random source. The example also does not demonstrate token rotation, revocation, secure Authorization-header parsing, or protection against session fixation. These omissions further weaken the authentication template, although insecure token generation cannot be conclusively confirmed from the available file.
Attack Path
- A developer copies the documented authentication pattern into an API endpoint.
- The application retrieves the complete user row using `select * ...[truncated 1346 chars]
- Remediation
View remediation
Remediation Suggestions
- Store passwords using a password-specific algorithm such as Argon2id, bcrypt, or scrypt with an appropriate work factor and a unique salt.
- Verify passwords using the hashing library's verification API rather than directly comparing stored and submitted strings.
- Remove MD5 from the password-storage recommendation because salting does not compensate for its speed and cryptographic weaknesses.
- Replace
select *with an explicit list of fields required for authentication. - Never serialize password hashes, plaintext passwords, reset tokens, recovery secrets, or internal security fields into an API response.
- Map the database record to an allowlisted response object containing only fields such as the user ID, username, and display name.
- Generate session tokens with a cryptographically secure random generator, apply sufficient entropy, and define expiration, revocation, and rotation behavior.
- Parse the Authorization header using an explicit scheme such as
Bearer, reject malformed values, and avoid using the entire unvalidated header as a cache key. - Add rate limiting, failed-login monitoring, and temporary lockout controls to reduce credential-guessing attacks.
- Use a hardened pattern similar to:
javascript var user = db.selectOne( "select id, username, password_hash, display_name from user where username = ?", body.username ); if (!user || !passwordHasher.verify(body.password, user.password_hash)) { return {code: 401, msg: "Username or password is incorrect"}; } var token = secureTokenGenerator(); cache.set("token:" + token, user.id, 3600); return { code: 200, data: { token: token, user: { id: user.id, username: user.username, displayName: user.display_name } } };
