Skip to content

Next.js security review

Review permissions inside your Next.js Server Actions.

Treat a Server Action as an endpoint. Verify the caller, validate untrusted inputs and check access to the resource before reading or changing it. A hidden button or a server-only file does not establish permission.

By Vibe App Scanner · Updated

Code scanning is included with Go from $29/month and Pro from $49/month for new accounts. Free accounts do not include repository scans. Existing account and AppSumo terms are unchanged.

Authentication is not resource ownership

Fictional action missing an ownership check
'use server';

export async function renameProject(id: string, title: string) {
  await requireUser(); // A signed-in caller, but which project can they edit?
  await db.project.update({ where: { id }, data: { title } });
}

A verified login only establishes identity. If a caller can choose another person’s project ID, the write also needs a resource permission check. For team projects, that may be a membership and role check rather than a single owner field.

Constrain the operation to the verified caller

Illustrative owner-only update pattern
'use server';
import { z } from 'zod';
import { db } from '@/lib/db';
import { requireUser } from '@/lib/auth';

const inputSchema = z.object({
  id: z.string().uuid(),
  title: z.string().trim().min(1).max(120),
});

export async function renameProject(raw: unknown) {
  const user = await requireUser();
  const input = inputSchema.parse(raw);
  const result = await db.project.updateMany({
    where: { id: input.id, ownerId: user.id },
    data: { title: input.title },
  });
  if (result.count !== 1) throw new Error('Project unavailable.');
  return { updated: true };
}

This authored example assumes a Prisma-style database client, an owner-only model and an application-specific requireUser helper that verifies the current request’s session on the server. Those imports are placeholders, not supplied VAS APIs or a complete drop-in implementation. Review the helper and schema; a security-sounding function name is not proof of authentication.

The ownership constraint is part of the write itself, rather than a separate UI check. The response returns a small confirmation instead of a complete database record. Adapt the policy for organization roles, shared projects and other legitimate access.

A review checklist for the full path

  • Identity: verify the session in the action or its server-side data-access layer; do not accept a caller-supplied owner ID as identity.
  • Authorization: check the requested record and operation. Test anonymous, wrong-owner and insufficient-role callers.
  • Input: validate IDs, allowed fields and size limits. Treat serialized action arguments and FormData as untrusted.
  • Output: return only the data that caller needs. Keep privileged clients, tokens and private fields out of client-visible results and errors.
  • Request protections: follow the deployed Next.js version’s origin and request-handling guidance. These protections complement application authorization; they do not replace it.
  • Evidence: review unresolved imports, wrappers and skipped checks in the source report. Verify the deployed version and test the actual entry point.

Check both denied and legitimate writes

  1. With disposable users A and B, create one private project for each.
  2. Confirm A can rename A’s project and receives only the expected confirmation.
  3. Submit B’s ID as A. Confirm B’s record is unchanged, including through the real action entry point.
  4. Repeat without a valid session, with malformed input and with any application-specific lower-privilege roles.
  5. Review the diff and test results before deploying. Rescan the source snapshot and use authorized live-app checks for deployed behavior.

Start with the GitHub workflow or compare the fictional source finding. For broader deployment concerns, use the existing Next.js security guide.

Questions about code scanning

Does use server protect an action from unauthorized callers?
No. The directive identifies server execution; it is not an application authorization policy. Validate the session and the caller’s access to the requested operation and record inside the server-side path.
Is hiding the submit button enough?
No. Client interface conditions do not enforce the server boundary. Test the action as a caller who should not have access, including requests for another user’s record.
Does a code report prove the action is exploitable or safe?
No. Supported source checks can supply evidence, but unresolved helpers, middleware, runtime identity and deployment differences limit what can be established. Use the report’s coverage notes and targeted tests.

Sources and scope

Product descriptions were checked against the linked vendor guidance on October 27, 2026. Features and plan availability can change. Examples on these pages are authored illustrations, not customer findings or a comparative accuracy benchmark.