Least-Privilege Isn't Enough to Govern AI Agent Writes
Least-privilege is the right first move, and this page is not an argument against it — scope every agent tightly, hand it no privileged service account, and you'll thank yourself. But access control answers one question — may this agent write here? — and there is a second one it structurally cannot answer: is this write correct? Inside any non-empty write grant, a hallucinated value or an injected instruction still produces SQL the permission allows, and it commits. Least-privilege bounds the blast radius; it never judges the value. OWASP's own guidance on Excessive Agency says as much — it pairs least-privilege with a human gate for high-impact actions. The two are halves of one answer, not competitors.
Two different questions
It's worth being precise about what each control decides, because the whole confusion comes from collapsing them:
- Access control (RBAC, least-privilege, scoped tokens) decides whether a principal may perform an operation on a resource. Output: allowed / denied. It reasons about identity and permission.
- A write-gate decides whether a specific proposed change should commit. Output: approve / reject, on a concrete before → after. It reasons about the value.
These aren't the same question wearing different clothes. "This agent is allowed to update customers.mrr" and "setting customers.mrr to 188,000,000 is correct" are independent facts, and least-privilege only ever establishes the first.
What least-privilege does well — credit where it's due
Give least-privilege its full due, because you need it regardless of what else you add. It shrinks the blast radius of anything that goes wrong: an over-broad token, a compromised prompt, a buggy tool. OWASP's LLM06 (Excessive Agency) makes it a headline mitigation — limit an extension's permissions to the minimum necessary, run on least-privilege identities tied to each user's context, never use a privileged service account for general model operations, and remove unused tools and functionality. That directly counters two of the three root causes OWASP names — excessive functionality and excessive permissions. Skipping least-privilege isn't an option this page endorses. It's the floor.
Where it stops: the granted scope is still non-zero
Here's the gap. Least-privilege reduces what the agent can touch — but for the agent to do its job, that set can't be empty. And within whatever remains, every write the permission allows is a write the permission will wave through, regardless of whether the value is right.
Grant an agent exactly one capability — UPDATE customers.mrr WHERE id = ?, nothing else — and you've done least-privilege well. The agent is still free to set mrr = 188,000,000 when it meant 1,880,000. No permission was exceeded; the token wasn't over-scoped; RBAC has nothing to object to. The write is authorized and wrong. Access control saw a permitted operation and did precisely its job — which is why it can't be the thing that catches this. You cannot tighten a permission until it means "only the correct value," because the permission doesn't know what the correct value is. Only a human (or a policy) looking at the specific change does.
Prompt injection turns a scoped, privileged path into a confused deputy
The gap gets actively exploited. A confused deputy is a program with legitimate authority tricked into misusing it on someone else's behalf — and a prompt-injected agent is the textbook case. The agent holds exactly the narrow write permission you granted; hostile text (in a support ticket it reads, a web page it browses, a row it queries) supplies the intent; and the agent dutifully issues a write that sits squarely inside its allowed scope. Least-privilege did its job — the write was within the grant — which is exactly why it didn't help. And you can't close the hole by sanitizing the input: national cyber-security guidance is explicit that prompt injection is not SQL injection and can't be fully filtered the way you parametrize a query. A boundary that assumes clean intent inside a valid permission is a boundary with a hole in it.
OWASP itself pairs least-privilege with a human gate
This isn't a contrarian take on the standard — it's what the standard says. OWASP LLM06 breaks Excessive Agency into three root causes: excessive functionality, excessive permissions, and excessive autonomy. Least-privilege attacks the first two. The third — autonomy — is a different lever: OWASP's remedy is human-in-the-loop, requiring approval for high-impact actions, with the model drafting the action and final execution depending on human confirmation. Updating records, publishing content, sending messages: draft by the agent, commit by a human. Least-privilege and a human gate are listed side by side because they cover different root causes. Doing one and calling it done leaves the third cause — autonomy over the actual write — unaddressed.
What each control actually decides
Laid out by the question each one answers:
| Control | Decides whether the agent MAY write? | Decides whether THIS write is correct? | Stops an injected write inside the allowed scope? | Fail-closed on a stale baseline? |
|---|---|---|---|---|
| RBAC / least-privilege | Yes — its job | No — value not examined | No — write is within scope | No |
| Scoped / JIT tokens | Yes — narrows the grant | No | No | No |
| Input filtering | Indirect | No — can't fully sanitize | Partial — misses novel injection | No |
| Read-only access | No — forbids writing | N/A — no writes | Yes — but can't do the work | N/A |
| Write-gate (value review) | Assumes least-privilege beneath it | Yes — approves before → after | Yes — nothing commits unapproved | Yes |
least-privilege gates the PERMISSION "may agent write customers.mrr?" ─▶ allowed │ agent writes mrr = 188,000,000 (authorized · wrong · committed) write-gate gates the VALUE proposed: mrr 1,240,000 → 188,000,000 ─▶ [ PENDING ] human: "that's 100× off" ─▶ reject proposed: mrr 1,240,000 → 1,880,000 ─▶ approve ─▶ commit
Do both — they're different layers
The takeaway isn't "replace least-privilege." It's "least-privilege plus a control that judges the write." Least-privilege sets the outer bound — the smallest set of tables and operations the agent could ever touch. A write-gate sits inside that bound and judges each specific change: the proposed write lands PENDING, a human (often a non-engineer who owns the data) approves the concrete before → after, it commits fail-closed against live state, and every step is recorded in a tamper-evident log. One caps the blast radius; the other stops the wrong-but-authorized write. Use both, and the "excessive autonomy" root cause OWASP names is finally covered. For where this sits relative to auditing a write after it lands, see Preventive vs Reactive AI Agent Governance; for the database specifics, human approval for AI agent database writes.
FAQ
Are you saying least-privilege is wrong or unnecessary?
No — the opposite. Least-privilege is necessary and you should apply it rigorously; OWASP makes it a headline mitigation for Excessive Agency. The claim is narrower: it's necessary but not sufficient. It bounds what an agent can touch; it doesn't judge whether a specific write within that bound is correct. Pair it with a control that does.
Can't I just scope the token tightly enough that nothing bad is possible?
Only by scoping it to nothing — and then the agent can't do its job. Any permission broad enough to let the agent write is broad enough to let it write a wrong value. The permission can't encode "only the correct number," because it doesn't know the correct number. That judgment lives at the value, not the grant.
Isn't this really a prompt-injection problem I should solve with input filtering?
Filtering helps at the margin but can't be the boundary: national cyber-security guidance is explicit that prompt injection can't be fully sanitized the way SQL injection can. Even with perfect filtering, an honest-but-hallucinated value still commits. A write-gate catches both the injected write and the merely-wrong one, because it reviews the value that would land — not the text that produced it.
Doesn't requiring approval defeat autonomy?
It bounds it where OWASP says to — high-impact actions. The agent still drafts everything; a human confirms the small set of writes that would be expensive to get wrong, and everything else commits freely. That's the "excessive autonomy" root cause handled without turning the agent back into a read-only tool.
How is a write-gate different from RBAC with an approval workflow bolted on?
An approval workflow that green-lights "the agent may run this tool" is still permission-shaped — it approves the call, not the resulting value, and doesn't re-check the target row at commit. A write-gate holds the concrete before → after in the system of record, routes it to the data owner, and commits fail-closed against live state. It approves the change, not the capability.
Penholder
Penholder is the second half. Keep your least-privilege scoping — Penholder adds the value gate: an agent's write lands PENDING and commits to Postgres, MySQL, a warehouse, SQL Server, Oracle, or a governed spreadsheet only after a human approves the exact before → after — fail-closed on conflict, with a tamper-evident log. The agent never holds raw credentials. See how it works →
Sources
- OWASP — LLM06:2025 Excessive Agency (root causes: excessive functionality, permissions, autonomy; mitigations: minimize permissions / least-privilege identities, and require human approval for high-impact actions) — and the OWASP Top 10 for LLMs (2025) PDF.
- Confused deputy — a program with legitimate authority induced to misuse it; the pattern MCP's own Security Best Practices flags for privileged proxies.
- Why input can't be the boundary — UK NCSC: Prompt injection is not SQL injection (it may be worse).
- The "God User" framing for read/write agent access — Rietta: Protect Production SQL Databases from AI/LLM Agentic SQL Query Risks.