AI agents
Credentials, Scopes, and Least Privilege
Design identity, authentication, authorization, and credential handling for tool-using agents.
By the end you can
- Define least-privilege agent access as an operational contract rather than a capability label
- Contrast Shared service credential with User-delegated credential in “A document agent exposed payroll files while summarizing a project folder”
- Trace “A secret in model context is already outside the security boundary” through a concrete execution path
- Produce “Threat-model one privileged tool” with evidence for “A trace can attribute every privileged action to a principal and task”
Visual
Every privileged call traces back to a principal who could authorize it
Every privileged call should trace back along a chain. A principal: the human, service, or organization whose authority is being delegated. An identity: a verifiable runtime or agent identity attached to the request. A scope, naming allowed resources, actions, destinations, and a time window. A credential broker, issuing or exchanging short-lived tokens after policy checks. And an audit trail, recording which principal, task, tool, and approval caused an effect. The broker and the trail are where ownership has to split, and the tests with it.
The last link is not a matter of house style. What an audit record has to contain was written down in 2020, as control AU-3 of NIST SP 800-53 Rev. 5. Five fields are the easy ones: what type of event occurred, when, where, the source of the event, and the outcome. Then a sixth, the "Identity of any individuals, subjects, or objects/entities associated with the event."
So the question to ask of an agent runtime is not whether it logs. It is whether all six fields can be filled in for a privileged tool call. Five of them a shared account answers well enough. The identity field it silently blanks, because every run produces the same answer.
- 1
Principal
The human, service, or organization whose authority is delegated.
- 2
Identity
A verifiable runtime or agent identity attached to the request.
- 3
Scope
Allowed resources, actions, destinations, and time window.
- 4
Credential broker
Issues or exchanges short-lived tokens after policy checks.
- 5
Audit trail
Records which principal, task, tool, and approval caused an effect.
Short-lived task-scoped credentials replace the broad service account
Authentication establishes who or what is calling. Authorization determines which resources and actions are allowed. An agent runtime should exercise delegated authority through short-lived, task-scoped credentials rather than broad developer or service accounts.
This is not an emerging best practice for agents. It is an existing numbered control, and its wording already anticipates a non-human caller. Least privilege is control AC-6 of NIST SP 800-53 Rev. 5: "Employ the principle of least privilege, allowing only authorized accesses for users (or processes acting on behalf of users) that are necessary to accomplish assigned organizational tasks." The parenthesis is the whole of the argument for agents. A process acting on behalf of a user is exactly what an agent runtime is. AC-6's discussion requires such processes to operate at privilege levels no higher than necessary. An agent handed a broad service account is not in a grey area of a control written for humans. It is out of compliance with a control written in 2020 that names its case.
The model should not receive raw secrets. Tools can hold credentials behind a policy boundary and expose only permitted operations.
Scope and lifetime are the two things a broad service account gets wrong at once: it grants more than the task needs and keeps granting it after the task ends.
Case
The rule is fifty years old, and it came with an enforcement clause
The principle predates the technology by half a century. Saltzer and Schroeder set out eight design principles in 1975, and two of the eight are the ones this lesson runs on. Both are one sentence long.
The first: "Least privilege: Every program and every user of the system should operate using the least set of privileges necessary to complete the job." Note that it says every program, not every user of a program. The 1975 formulation already binds the running process, not only the person behind it.
The second is its enforcement condition: "Complete mediation: Every access to every object must be checked for authority." A scope that is issued but consulted only on the first call of a session is not least privilege. It is least privilege announced once.
The OWASP Top 10 for LLM Applications 2025 applies both to agents, under LLM06, Excessive Agency. It asks that extensions be granted "only the minimum access needed" — AC-6 and Saltzer and Schroeder restated for tools. It also asks for OAuth, so that "actions taken on behalf of a user are executed on downstream systems in the context of that specific user". A shared service credential cannot satisfy that at all. There is no specific user's context for it to run in.
Example
A poisoned document turned a summarization request into a Google Drive key search
The victim does one ordinary thing. They upload a document and ask for a summary. The document carries an invisible prompt-injection payload, set in 1px white font. The injected instructions tell ChatGPT to search the connected Google Drive for API keys, and to place them in the URL parameters of a markdown image hosted on Azure Blob storage. Rendering the image sends the keys to the attacker. The user clicks nothing.
That chain is AgentFlayer, a zero-click exfiltration attack on ChatGPT Connectors. Zenity Labs demonstrated it at Black Hat USA 2025 and published it on 6 August 2025. The researcher, Tamir Ishay Sharbat, puts the setup plainly: "So as attackers all we need to do is poison a document with an invisible prompt injection payload (as seen in the image below) and wait for someone to upload it into ChatGPT."
Read it against the chain in the first section. Nothing in this attack defeats authentication. The identity was genuine. The connector scope was genuinely granted. The credential behaved exactly as issued. What the attack exploited is the gap between the scope the connector held and the scope the summarization task needed: the whole of a connected Drive, against one document the user had just uploaded.
- Decision at stake: how identity, authentication, authorization and credential handling are designed for a tool-using agent — specifically, whether a connector's scope is fixed at grant time or narrowed to the task in front of it.
- Hidden assumption: that a system prompt can reliably prevent misuse of broad credentials. AgentFlayer's payload is instructions in a document the user supplied themselves, and the document is read after the system prompt.
- Primary control question: a secret in model context is already outside the security boundary. Here the secrets never began in context. A broad Drive scope let the model fetch them into it, and a permitted outbound render carried them out.
- Evidence to collect: a trace can attribute every privileged action to a principal and task. AU-3's six fields are the checklist: for the Drive search that returned the API keys, can you name the task it belonged to, or only the identity that ran it?
Analogy
A Hotel Key Card, Not the Master Key
Hotels hand out a card for one room and a limited stay rather than the building's master key. The card is useful precisely because its authority is narrow and revocable.
A task-scoped capability can go further than any key card. It constrains destinations, amounts, and the sequence in which actions may be taken.
What the master key costs when it turns has a number attached to it. Microsoft published CVE-2025-32711 on 11 June 2025, "M365 Copilot Information Disclosure Vulnerability", and scored it itself: CVSS 3.1 base 9.3, classified CWE-74 injection. Its description runs to one line: "Ai command injection in M365 Copilot allows an unauthorized attacker to disclose information over a network." Aim Security, which reported the flaw and named it EchoLeak, stated the same day that it allowed sensitive and proprietary information to be exfiltrated from M365 Copilot without any user interaction.
Read the vector rather than the score. It is CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N. PR:N and UI:N mean no privileges and no user interaction were required. S:C means the scope changed: the compromise did not stay inside the component that was attacked. The blast radius of a confused assistant is not a matter of how badly it was confused. It is whatever its own access scope contained.
Least privilege converts model compromise from organization-wide access into a bounded incident.
Comparison
Where designs for least-privilege agent access diverge
What an attacker gets when the model is confused depends on which credential the runtime handed over. There are three to choose between.
A shared service credential is one broad account reused across many runs: simple to integrate, large blast radius, weak attribution. A user-delegated credential has the runtime act inside the current user's permission set. Ownership is aligned, and it is the arrangement OWASP asks for when it requires actions to run in that specific user's context. It still needs consent and expiry, and it can still be overbroad. The AgentFlayer victim's Drive connector was delegated, and it held far more than a summary needed. A task-scoped capability is a short-lived token permitting one bounded operation: least privilege in the AC-6 sense, strong attribution, more infrastructure to build.
The difference shows up afterwards, in the record the call leaves behind. AU-3's identity field is the test. A shared service credential makes every call look identical, so nothing can be pinned to anyone. A delegated or task-scoped one lets a trace attribute every privileged action to a principal and task. And if the credential was pasted into the prompt at any point, the distinction stops mattering. The runtime is no longer the thing deciding what may be used.
Shared service credential
One broad account is reused across many runs.
- Simple integration
- Large blast radius
- Weak attribution
User-delegated credential
The runtime acts within the current user’s permission set.
- Aligned ownership
- Requires consent and expiry
- Can still be overbroad
Task-scoped capability
A short-lived token permits one bounded operation.
- Least privilege
- Strong attribution
- More infrastructure
Key idea
A secret in model context is already outside the security boundary
Prompts, traces, retries, and tool results may be stored or inspected. Supplying raw API keys to the model creates unnecessary exfiltration paths. This is not a theoretical worry. Text written for one purpose gets mined for credentials as a matter of routine, and there is a dated case with a count in it.
Compromised OAuth tokens for the Salesloft Drift third-party application were used to export data from corporate Salesforce instances between at least 8 and 18 August 2025. Google Threat Intelligence Group tracks the actor as UNC6395, and assessed the primary intent as harvesting credentials. The actor searched the exfiltrated text for AWS access keys (AKIA), passwords, and Snowflake access tokens. None of that text was a secret store. It was support correspondence.
Cloudflare was one of the affected customers. It searched the stolen copy of its own support cases and found 104 Cloudflare API tokens. It rotated all 104 in an abundance of caution, having found no suspicious activity associated with any of them. Then, on 2 September 2025, it told its customers plainly that "any information that a customer may have shared with Cloudflare in our support system—including logs, tokens or passwords—should be considered compromised, and we strongly urge you to rotate any credentials that you may have shared with us through this channel."
A model's prompt history, tool results, and retry logs are the same kind of channel as a support ticket: a text store nobody classified as a credential store, holding credentials. Keep secrets in trusted execution components. Enforce destination and action scopes. Rotate credentials independently of prompts.
Once a key has passed through a prompt, rotating it is the only honest response, and nothing tells you when that moment arrived.
Steps
Threat-model one privileged tool
Threat-model a single privileged tool. Ask what a confused deputy could reach through it, and how far. Work the chain in order: identify principals, separating end user, runtime, tool service, and resource owner; enumerate permissions across read, write, send, delete, approve, and administer; reduce scopes, binding access to named resources, destinations, amounts, and expiry; protect secrets, using brokers or backend integrations instead of model-visible credentials; and exercise revocation against expiry, user logout, policy change, and incident shutdown.
Two answers matter more than the rest. First, list every place the tool's credential is handled, and treat any prompt, system message or retrieved document on that list as a finding in itself. Second, check whether the record the call leaves behind names both the principal the work was done for and the task it belonged to. That is AU-3's identity field, filled in.
The revocation step has a worked example with dates on it. A threat actor obtained access to a Heroku database on 7 April 2022 and downloaded stored customer GitHub integration OAuth tokens; Heroku's own incident review supplies that date. GitHub Security opened its investigation five days later. Mike Hanley published the finding on 15 April 2022: "On April 12, GitHub Security began an investigation that uncovered evidence that an attacker abused stolen OAuth user tokens issued to two third-party OAuth integrators, Heroku and Travis-CI, to download data from dozens of organizations, including npm."
Count the days, then read the response. On 15 April 2022 Heroku revoked all existing tokens from the Heroku Dashboard GitHub integration and blocked new ones from being created. Mass revocation was the only move available. The tokens were long-lived, stored, and not scoped to any task that had since ended. That is what the revocation step asks a team to rehearse before it is needed: not whether revocation exists, but how much has to go at once when it is used in anger.
- 1
Identify principals
Separate end user, runtime, tool service, and resource owner.
- 2
Enumerate permissions
List read, write, send, delete, approve, and administer actions.
- 3
Reduce scopes
Bind access to named resources, destinations, amounts, and expiry.
- 4
Protect secrets
Use brokers or backend integrations instead of model-visible credentials.
- 5
Exercise revocation
Test expiry, user logout, policy change, and incident shutdown.
A permission nobody can attribute is one nobody can withdraw
Credential design should assume the model can be confused by untrusted content. The goal is to limit what a confused deputy can cause. The confused deputy is not a figure of speech. Norm Hardy named it in 1988, and the incident he describes is older than the paper: he dates it to "about eleven years ago" at Tymshare, that is, around 1977.
A FORTRAN compiler sat in directory SYSX and was invoked as "RUN (SYSX)FORT". It had been marked with homefiles license so that it could write its statistics file, (SYSX)STAT. A user supplied (SYSX)BILL — the billing information file, in the same directory — as the name of the file to receive debugging output. The operating system allowed the write, because the compiler held the license. The billing information was lost.
Hardy's diagnosis is the sentence this whole lesson is a gloss on: "The fundamental problem is that the compiler runs with authority stemming from two sources." One source was the user's request. The other was the compiler's own standing license. Nothing in the call distinguished them.
An agent with a broad service credential is that compiler. The user's words supply the destination, the standing credential supplies the authority, and the system cannot tell which of the two it is obeying. Hardy's remedy is the third column of the comparison above: a capability that both designates the resource and authorizes the write, so that a request without the capability cannot name a target it was not given.
Two rules follow. A secret in model context is already outside the security boundary, so credentials belong to the runtime that makes the call and never to the text the model reads. And every grant should leave a record specific enough to name the principal it acted for and the task it served — AU-3's identity field is the minimum, not the aspiration. A permission nobody can attribute is a permission nobody can withdraw.
Assume the model will be talked into the wrong call eventually, and the only question left is how much its credentials let it do.
Key takeaways
- Authentication establishes who or what is calling; authorization decides what it may reach. Saltzer and Schroeder paired least privilege with complete mediation in 1975, and that pairing means the second check happens on every access, not once per session.
- The model should not receive raw secrets. UNC6395 mined stolen Salesforce support text for AWS access keys and Snowflake tokens, and Cloudflare had to rotate 104 API tokens that had merely passed through a support channel.
- Least privilege for an agent runtime is a written control, not a preference: NIST SP 800-53 Rev. 5 control AC-6 extends it to “processes acting on behalf of users”.
- Attribution is a written control too. AU-3 requires an audit record to carry the event type, time, location, source, outcome, and the identity associated with the event — and a shared service credential blanks the last one.
- Keep secrets in trusted execution components, enforce destination and action scopes, and rotate credentials independently of prompts. AgentFlayer reached a connected Google Drive from an uploaded document, with no user click.
- Credential design should assume the model can be confused by untrusted content. Hardy's Tymshare compiler ran “with authority stemming from two sources” and destroyed (SYSX)BILL; a broad service credential puts an agent in exactly that position.