Skip to content

🔒 Mass assignment on signup (emailVerified/providerData client-settable) #3850

@PierreBrisorgueil

Description

@PierreBrisorgueil

Part of #3848 · severity: high · related: #3825

Where

modules/users/models/users.schema.js (fields 26-27, 39-40, 42, 48-50), modules/auth/routes/auth.routes.js:73, modules/auth/controllers/auth.controller.js:119.

Problem

The signup route validates with model.isValid(UsersSchema.User). The model middleware replaces req.body with the full Zod-parsed User, so every optional schema field is client-writable on signup: emailVerified, providerData, additionalProvidersData, resetPasswordToken/Expires, failedLoginAttempts/lockUntil/lastLoginAt. The controller only overrides roles.

Exploit

  • Email-verification bypass: POST /api/auth/signup { …, "emailVerified": true } creates a verified account, defeating verification gates (org creation, join requests, domain search).
  • OAuth account pre-hijacking: pre-register victim@domain with emailVerified:true and seeded providerData; when the real victim later signs in with the provider, the lookup hits the attacker-seeded record.

Fix

Don't use the full Mongoose-mirroring User schema as the signup write surface. Add a dedicated signup Zod schema with only client-settable fields (firstName, lastName, email, password, optional bio/position/terms), .strict(). Defense in depth: in the controller force emailVerified:false and delete the server-owned fields before UserService.create. The server must own identity/trust fields. (Cross-refs #3825, the analogous email-change path.)

Created via /dev:issue

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions