Transactions and functions limited to authorized users
Why it matters
CMMC 2.0 AC.L1-3.1.2 (NIST 800-171r2 3.1.2) requires that users are limited to the specific transactions and functions they are authorized to execute — not merely authenticated to the system. Without ownership or role checks on write operations, any logged-in user can modify or delete records belonging to others, a vulnerability OWASP A01 and CWE-285 classify as Broken Access Control. In FCI-handling systems this creates an audit trail problem: if user A can overwrite user B's contract documents, data integrity and individual accountability both collapse — both mandatory CMMC concerns.
Severity rationale
High because an authenticated-but-unauthorized write can corrupt or delete another user's FCI without any privilege escalation step.
Remediation
Add an ownership or role check immediately after the session check in every mutation handler. Authentication and authorization are two separate gates — passing the first does not satisfy the second:
// app/api/documents/[id]/route.ts
export async function PUT(req: Request, { params }: { params: { id: string } }) {
const session = await getServerSession()
if (!session) return Response.json({ error: 'Unauthorized' }, { status: 401 })
const document = await db.document.findUnique({ where: { id: params.id } })
if (!document) return Response.json({ error: 'Not found' }, { status: 404 })
// Ownership check — not just "is there a session?"
if (document.userId !== session.user.id && session.user.role !== 'admin') {
return Response.json({ error: 'Forbidden' }, { status: 403 })
}
const body = await req.json()
const updated = await db.document.update({ where: { id: params.id }, data: body })
return Response.json(updated)
}
Report the ratio of mutation endpoints covered — partial coverage is still a CMMC failure under AC.L1-3.1.2.
Detection
-
ID:
transaction-control -
Severity:
high -
CMMC Practice: AC.L1-3.1.2
-
What to look for: Examine authorization checks on write and mutate operations. Look at every POST, PUT, PATCH, and DELETE handler — after verifying that a session exists (authentication), also verify that the authenticated user is authorized to perform that specific action (authorization). Look for role checks, ownership verification (e.g.,
if (record.userId !== session.user.id)), or permission gates. Check for any mutation endpoint that only confirms a valid session without also checking whether the user has permission to modify the targeted resource. -
Pass criteria: Count all mutation endpoints (POST, PUT, PATCH, DELETE) and check each for authorization logic. All mutation endpoints verify user authorization beyond just authentication. At least 90% of sensitive write operations have server-side permission or ownership checks. Report the ratio: "X of Y mutation endpoints have authorization checks."
-
Fail criteria: Write operations only confirm the user is logged in but do not check whether they are allowed to perform the specific action. Must not pass when ownership checks are present on some endpoints but absent on others — partial coverage is still a fail.
-
Skip (N/A) when: The project has no write operations or data mutations (fully read-only).
-
Detail on fail: Name the specific endpoints lacking authorization checks. Example:
"PUT /api/documents/[id] only checks session exists but not whether session.user.id matches document.userId. Any authenticated user can edit any document."Keep under 500 characters. -
Remediation: Add ownership or role verification to every mutation handler:
// app/api/documents/[id]/route.ts export async function PUT(req: Request, { params }: { params: { id: string } }) { const session = await getServerSession() if (!session) { return Response.json({ error: 'Unauthorized' }, { status: 401 }) } // Authorization: verify ownership, not just authentication const document = await db.document.findUnique({ where: { id: params.id } }) if (!document) { return Response.json({ error: 'Not found' }, { status: 404 }) } if (document.userId !== session.user.id && session.user.role !== 'admin') { return Response.json({ error: 'Forbidden' }, { status: 403 }) } // Proceed with update const body = await req.json() const updated = await db.document.update({ where: { id: params.id }, data: body }) return Response.json(updated) }
External references
- cmmc:2.0 · AC.L1-3.1.2 — Transaction & Function Control
- cwe · CWE-285 — Improper Authorization
- owasp:2021 · A01 — Broken Access Control
- nist:rev2 · SP-800-171 3.1.2 — Limit system access to the types of transactions and functions authorized users are permitted to execute
Taxons
History
- 2026-04-18·v1.0.0·Initial import from gov-cmmc-level-1·automated