FinanceGadget
Guide

Securing an AI Agent That Has Tool Access: An Architecture Guide

The short answer

Giving an AI model “tool access”—allowing it to execute shell commands, run database queries, call external APIs, or browse the web—transforms it from a passive text generator into an autonomous agent.

If an autonomous agent encounters an indirect prompt injection attack (for example, reading a malicious instruction inside a webpage or email), it can execute arbitrary tools on behalf of the attacker. Securing an AI agent requires strict privilege separation, sandboxed execution environments, human-in-the-loop (HITL) approval gates for destructive actions, and deterministic input validation.

The Dual Risk of Tool-Enabled AI Agents

AI agents face two primary security vulnerabilities:

  1. OWASP LLM06: Excessive Agency: Giving an agent more permissions, capabilities, or tool access than necessary for its task. For example, giving a code-formatting agent permission to execute arbitrary bash commands or drop database tables.
  2. OWASP LLM01: Indirect Prompt Injection: An attacker places hidden instructions inside data that the agent processes (such as a GitHub issue, PDF document, or website). When the agent reads the file, it executes the hidden instructions using its connected tools. Read our detailed guide on prompt injection explained.

The 5 Security Architecture Principles for AI Agents

To deploy tool-enabled AI agents safely, implement these five architectural controls:

┌──────────────────────────────────────────────────────────┐
│                   Untrusted Data Source                  │
│             (Webpage, Email, User Prompt)                │
└────────────────────────────┬─────────────────────────────┘


┌──────────────────────────────────────────────────────────┐
│                   LLM Reasoning Engine                   │
│             (Generates Intent / Tool Call)               │
└────────────────────────────┬─────────────────────────────┘


┌──────────────────────────────────────────────────────────┐
│             Deterministic Security Gateway               │
│        (Schema Validation & Permission Checks)           │
└────────────────────────────┬─────────────────────────────┘


┌──────────────────────────────────────────────────────────┐
│              Human-in-the-Loop Gate (HITL)               │
│         (Requires Approval for Write/Delete/Exec)        │
└────────────────────────────┬─────────────────────────────┘


┌──────────────────────────────────────────────────────────┐
│              Isolated Execution Sandbox                  │
│       (Ephemeral Container, Read-Only FS, No Network)    │
└──────────────────────────────────────────────────────────┘

1. Principle of Least Privilege for Tools

Scope tool capabilities as tightly as possible:

  • Replace open-ended tools (e.g., execute_bash()) with specific, single-purpose functions (e.g., format_json_file(filename)).
  • Never pass database connection strings with DROP, ALTER, or DELETE permissions to an LLM. Use read-only database replicas for query tools.

2. Human-in-the-Loop (HITL) Approval Gates

Classify agent actions into Read (Safe) and Write/Execute (Sensitive) tiers:

  • Read Actions (e.g., searching documentation, reading a file): Executed automatically.
  • Write/Execute Actions (e.g., sending emails, executing shell scripts, modifying database records, creating PRs): Require explicit human approval via UI prompt before execution.

3. Ephemeral Sandbox Execution

Run code execution tools inside short-lived, isolated environments:

  • Execute code in ephemeral Docker containers or gVisor microVMs with strict memory and CPU limits.
  • Strip network access from execution sandboxes unless outbound internet access is explicitly required for the task.
  • Mount filesystems as read-only, except for designated temporary working directories.

4. Deterministic Schema Validation

Do not allow the LLM to construct raw command strings. Require the model to output structured JSON matching a strict JSON Schema, and validate parameters deterministically before execution.

5. Input & Context Isolation

Keep system instructions and user input in separate structured payload fields (e.g., system vs user role in API requests). Never concatenate untrusted web text into system prompt templates.

Agent Security Controls Assessment Matrix

Agent CapabilityPrimary Attack VectorMandatory Security Control
Shell / Bash AccessCommand injection & system takeoverEphemeral Docker sandbox + Read-only FS + HITL
Database QueriesSQL injection & data exfiltrationRead-only connection + Strict parameter schemas
Web BrowsingIndirect prompt injectionStrip HTML scripts + Block local IP ranges (SSRF)
API Webhooks / WriteData deletion / Unauthorized postHuman approval gate + OAuth scope restriction

The three-way combination that creates the risk

The controls above are individually useful, but there is a simpler framing that tells you when you need them, and it is the most practically useful idea in agent security.

Serious exposure requires three things simultaneously:

  1. Access to private data — repositories, internal APIs, a customer database, your email.
  2. Exposure to untrusted content — anything the agent reads that an attacker could have written: a web page, a GitHub issue, an inbound email, a PDF, a dependency’s README.
  3. A way to communicate outward — an HTTP request, a message send, a commit, a rendered image URL, a webhook.

Any two of these is usually manageable. All three together means an attacker who controls the untrusted content can instruct the agent to read private data and send it somewhere, and no amount of prompting will reliably prevent it.

This gives you a design test to apply before building anything: can I remove one of the three? An agent that reads untrusted web pages and has no private data access is largely harmless. One with deep internal access that only ever processes content your own systems produced is a much narrower problem. One that can read anything and reach the internet needs every control in this article and still warrants caution.

The exfiltration channel is the one people forget, and it is rarely a deliberate tool. Markdown image rendering, a URL preview, a DNS lookup, an error message posted to an external service, or a “helpful” link the agent constructs will all carry data out. Enumerate the outbound paths, not just the tools.

The agent is an identity, and it needs treating as one

The most common architectural mistake is running an agent with a single long-lived service credential that has union-of-everyone permissions, because that was easiest during development.

An agent acting on behalf of a user should carry that user’s authority and no more. If it acts on its own behalf, it needs its own identity with its own scoped, rotated credentials, listed in your access review, and deprovisioned like any other account.

Three properties are worth engineering deliberately:

Permissions derived from the requesting user, not the agent. Otherwise the agent becomes a privilege-escalation path — a user asks it to fetch something they could not access directly, and it obliges. This is the confused-deputy problem and it is the failure mode auditors ask about.

Credentials the model never sees. Tokens belong in the execution layer, not in context. A model that can read a credential can be induced to emit it.

Short-lived, narrowly scoped tokens per task, rather than one standing credential. If a task needs read access to one repository for ninety seconds, that is what it should hold.

Where human approval actually helps, and where it fails

Approval gates are necessary and they are not sufficient, for a reason that has nothing to do with the technology.

Approval fatigue is real and predictable. An agent that requests confirmation twenty times an hour trains its operator to approve without reading, and the twenty-first request is the one that matters. This is the same failure described in MFA fatigue, arriving through a different door.

Two design rules follow. Ask rarely, and make each request meaningful — auto-approve genuinely safe reads, and reserve the interrupt for actions that are destructive, outbound, or irreversible. And show the reviewer what they are actually approving: the resolved command, the exact target, the diff, the recipient. “Agent wants to run a tool — approve?” is unanswerable, in the same way a bare push notification is.

Where an action is irreversible, prefer a design that makes it reversible instead of one that asks harder: write to a branch rather than the default, stage a draft rather than sending, soft-delete rather than delete. An undo is worth more than a prompt.

Logging, because you will need to reconstruct this

When something goes wrong with an agent, the question is always “what did it actually do, and why”. Answering it requires logs that most implementations do not keep.

Record, for every run: the full prompt and context assembled, every tool call with its resolved parameters, every result returned, the identity the action executed under, and every approval decision with who made it and what they were shown. Keep the correlation identifier that lets you tie a single user request to the entire chain of downstream actions.

Two constraints apply. These logs contain whatever the agent processed, so they inherit its data sensitivity — they need the same access controls and a defined retention period, and they are in scope for access requests, as prompt logs and data subject access requests covers. And they must be written somewhere the agent itself cannot modify.

Testing it before it tests you

Agent security cannot be assured by reading the code, because the failure mode is behavioural. Build the adversarial cases into your test suite:

  • Plant injection payloads in every content source the agent ingests — a repository file, a web page it will fetch, a document, an inbound message — and assert that no tool call results.
  • Assert that the agent cannot reach data outside the requesting user’s permissions, by asking it to.
  • Verify the sandbox actually holds: attempt network egress, filesystem writes outside the working directory, and access to instance metadata endpoints.
  • Confirm that removing the approval gate is not silently possible through a configuration path.
  • Re-run all of it when you change the model, the system prompt, or the tool definitions. Behaviour is not stable across any of those.

Assume that some injection attempts will succeed, and design so that success is survivable rather than catastrophic. That is what the least-privilege and sandboxing work buys you: not prevention, but a bounded blast radius when prevention fails.

To review broader AI application vulnerabilities, see our guide on the OWASP LLM Top 10 explained, and RAG security: what leaks and how for the retrieval layer most agents depend on.