T05 · Unauthorized Access and Privilege Escalation
- Location
references/examples.md:72- Finding
Server Action Allows User Modification Without Server-Side Authorization
- Content
View full analysis
Vulnerability Details
File Location:
references/examples.md, lines 72–102
Vulnerability Type: Insecure direct object reference and missing function-level authorization
Risk Level: HighVulnerable Code
tsx export async function updateUser(userId: string, formData: FormData) { const validatedFields = UpdateUserSchema.safeParse({ name: formData.get('name'), email: formData.get('email'), }); if (!validatedFields.success) { return { errors: validatedFields.error.flatten().fieldErrors, }; } try { await db.user.update({ where: { id: userId }, data: validatedFields.data, }); revalidatePath('/profile'); revalidateTag('user'); } catch (error) { return { error: 'Failed to update user' }; } redirect('/profile'); } // Using Server Action in form // app/profile/edit/page.tsx export default function EditProfile({ user }: { user: User }) { const updateUserWithId = updateUser.bind(null, user.id);Technical Analysis
The Server Action accepts a
userIdand uses it directly as the database update selector. Although the submitted fields are schema-validated, the action does not authenticate the caller or verify that the authenticated principal owns the specified account. Input validation does not replace authorization.Binding the identifier when rendering the form is not, by itself, an authorization control. Server Actions are remotely reachable server endpoints, and access control must be enforced inside the action. If an attacker can invoke the action with a different identifier, or if an untrusted value reaches the bound argument, the database operation will update the selected user without an ownership or role check.
Attack Path
- An attacker authenticates as an ordinary user or otherwise obtains access to the exposed Server Action.
- The attacker identifies the action endpoint and ...[truncated 880 chars]
- Remediation
View remediation
Remediation Suggestions
- Authenticate inside the Server Action using a trusted server-side session.
- Derive the target user ID from the verified session for self-service profile updates rather than accepting it as an action argument.
- If administrators may update other accounts, perform an explicit role and permission check before the database operation.
- Reject unauthenticated requests and unauthorized target-user combinations before processing form data.
- Retain strict schema validation and normalize sensitive fields such as email addresses.
- Require appropriate re-verification before changing security-sensitive identity attributes.
- Record security audit events for profile and email changes without logging sensitive form data.
A safer design is:
tsx export async function updateCurrentUser(formData: FormData) { const session = await getServerSession(authConfig); if (!session?.user?.id) { throw new Error('Unauthorized'); } const validatedFields = UpdateUserSchema.safeParse({ name: formData.get('name'), email: formData.get('email'), }); if (!validatedFields.success) { return { errors: validatedFields.error.flatten().fieldErrors, }; } await db.user.update({ where: { id: session.user.id }, data: validatedFields.data, }); revalidatePath('/profile'); }
