11.2 Tool Permissions, Sandboxing & Least Privilege

Key Takeaways

  • The Principle of Least Privilege dictates that agent tools must be scoped to narrow, intent-specific operations with strict parameter constraints rather than broad, omnibus system access.
  • Server-side tool execution handlers must independently enforce authentication, authorization (RBAC/ABAC), and session constraints; developers must never trust Claude's decision to call a tool as proof of user authorization.
  • Read-only queries must be segregated from destructive mutations by routing search tools to read-only database replicas and enforcing parameterized SQL queries to prevent secondary injection.
  • High-stakes and irreversible operations (e.g., financial transactions, data deletion, external messaging) must implement interruptible Human-in-the-Loop (HITL) approval gates with state persistence.
  • Code execution tools must run within isolated, ephemeral sandboxes (such as gVisor or Firecracker microVMs) configured with read-only root filesystems, dropped Linux capabilities, and blocked network access to cloud metadata endpoints (169.254.169.254).
Last updated: September 2026

Tool Permissions, Sandboxing & Least Privilege

Exam Blueprint Focus: Equipping Claude with tools transforms a passive reasoning model into an active, autonomous software agent. On the Anthropic Claude Certified Developer - Foundations (CCDV-F) examination, candidates are extensively tested on the architectural boundaries governing tool security. Key proficiencies include designing fine-grained tool schemas under the Principle of Least Privilege, enforcing independent server-side authorization, architecting Human-in-the-Loop (HITL) approval gates for high-stakes actions, and isolating dynamic code execution inside hardened sandboxes with blocked cloud metadata access.


The Principle of Least Privilege in Tool Architecture

The Principle of Least Privilege (PoLP) is the foundational security doctrine requiring that every module, process, or agent be granted only the minimum access privileges absolutely necessary to fulfill its designated business objective. When designing tools for Claude, developers must resist the anti-pattern of creating powerful, general-purpose "god tools" in favor of tightly bounded, purpose-specific interfaces.

Anti-Pattern: The Omnibus Tool

An omnibus tool exposes broad, unconstrained capabilities to the LLM. Consider an agent tasked with updating customer addresses that is provided with an omnibus database tool:

// DANGEROUS ANTI-PATTERN: Omnibus Database Tool
{
  "name": "execute_database_query",
  "description": "Executes an arbitrary SQL query against the production database.",
  "input_schema": {
    "type": "object",
    "properties": {
      "query": {"type": "string", "description": "The raw SQL query to run."}
    },
    "required": ["query"]
  }
}

If the model suffers a prompt injection or hallucinates an incorrect table name, this single tool allows table drops (DROP TABLE users;), full credential dumping (SELECT * FROM api_keys;), or cross-tenant data corruption. The blast radius is total and unconstrained.

Best Practice: Narrow Intent-Specific Tooling

Under Least Privilege, the same capability is decomposed into a narrow, strongly-typed tool that accepts only validated domain identifiers and specific attributes:

// SECURE PATTERN: Scoped, Intent-Specific Tool
{
  "name": "update_customer_shipping_address",
  "description": "Updates the verified shipping address for an active customer order.",
  "input_schema": {
    "type": "object",
    "properties": {
      "order_id": {
        "type": "string",
        "pattern": "^ORD-[0-9]{6,10}$",
        "description": "The unique order identifier."
      },
      "street_address": {"type": "string", "maxLength": 100},
      "city": {"type": "string", "maxLength": 50},
      "postal_code": {"type": "string", "pattern": "^[0-9]{5}(-[0-9]{4})?$"}
    },
    "required": ["order_id", "street_address", "city", "postal_code"]
  }
}

Dynamic Tool Registration Based on User Roles

Least Privilege also applies dynamically at runtime. Rather than declaring an identical tool manifest for all users, the application server should inspect the authenticated caller's Role-Based Access Control (RBAC) profile before constructing the /v1/messages request.

If a standard customer initiates a session, administrative tools (such as issue_refund, reset_credentials, or modify_account_tier) must be completely omitted from the tools parameter array. The model cannot hallucinate or be tricked into invoking a tool that does not exist in its runtime definition.


Independent Server-Side Authorization: The Zero-Trust Tool Principle

A critical conceptual requirement for the CCDV-F examination is the Zero-Trust Tool Principle:

The Cardinal Rule of Agent Security: An LLM is a non-deterministic reasoning and planning component, NOT a security perimeter. A model's decision to invoke a tool must NEVER be treated as authorization to perform the requested action!

When Claude outputs a tool_use content block, it is merely proposing an intention. The client application's backend tool execution handler must independently enforce authentication, authorization, and tenant isolation before executing any logic.

[User Request] --> [Claude Model]
                        |
                        v
               [Emits tool_use block]
               name: 'delete_order'
               input: { 'order_id': 'ORD-999', 'user_id': '123' }
                        |
                        v
+-------------------------------------------------------------+
| SERVER-SIDE TOOL HANDLER (Zero-Trust Enforcement Point)     |
| 1. Extract verified User ID from session JWT (NOT from LLM) |
| 2. Query IAM / RBAC: Does User 123 own Order ORD-999?       |
| 3. Check Tenant ID: Does Order belong to User's Tenant?     |
| 4. Validate CSRF / Session expiration                       |
+-------------------------------------------------------------+
        |                                       |
   [AUTHORIZED]                            [FORBIDDEN]
        |                                       |
        v                                       v
[Execute Deletion &]               [Return tool_result with]
[Return tool_result]               [is_error: true & Reason]

Preventing Parameter Injection and Identity Spoofing

Never permit Claude to dictate the identity of the acting user through tool parameters. If a tool schema includes a user_id or tenant_id property, an attacker can use prompt injection to manipulate Claude into providing another user's ID, resulting in Insecure Direct Object Reference (IDOR) vulnerabilities.

Instead, the tool execution handler must extract the authenticated identity directly from the cryptographically signed session token (such as a verified JWT or server-side session store) and override or bind the operation strictly to that context.


Segregation of Read and Write Pathways & SQL Injection Defense

Production database architectures must physically segregate analytical read tools from state-mutating write tools:

  1. Read-Only Database Replicas: Tools designed for informational retrieval, vector search, or report generation must connect exclusively to read-only database replicas using database user credentials that possess only SELECT privileges. Even if a prompt injection attack succeeds in crafting a destructive statement, the database engine enforces an immediate PERMISSION DENIED at the socket level.
  2. Strict Parameterized Queries: Tool handlers must never construct database queries using raw string concatenation or formatted f-strings with LLM-provided arguments. All database queries executed by tool handlers must utilize parameterized prepared statements or Object-Relational Mapping (ORM) query builders to eliminate secondary SQL injection.
# VULNERABLE TOOL HANDLER: String Concatenation SQL Injection
def handle_lookup_order_vulnerable(tool_input):
    order_id = tool_input["order_id"]
    # If order_id is "ORD-123' OR '1'='1", full table is exposed!
    query = f"SELECT * FROM orders WHERE order_id = '{order_id}'"
    return db.execute(query)

# SECURE TOOL HANDLER: Parameterized Prepared Statement
def handle_lookup_order_secure(tool_input, session_user_id):
    order_id = tool_input["order_id"]
    # Parameterized query with session user scoping
    query = "SELECT order_id, status, amount FROM orders WHERE order_id = %s AND user_id = %s"
    return db.execute_parameterized(query, (order_id, session_user_id))

Human-in-the-Loop (HITL) Approval Gates for High-Stakes Actions

Certain operational categories are inherently irreversible or carry profound legal, financial, or operational consequences. For these high-stakes actions, autonomous agents must not be permitted to execute actions unilaterally. Systems must implement an interruptible Human-in-the-Loop (HITL) approval gate.

Categorizing Operations by Risk Profile

  • Tier 1: Low-Risk Operations (Fully Autonomous): Reading public documentation, performing semantic search, formatting reports, calculating mortgage amortization tables. Automatically executed by tool handlers.
  • Tier 2: Medium-Risk Operations (Autonomous with Audit Logging): Updating customer preferences, adding item tags, drafting email responses to internal review queues. Executed automatically, but logged to immutable audit streams.
  • Tier 3: High-Risk Operations (Mandatory HITL Gate): Authorizing financial disbursements, initiating wire transfers, modifying DNS records, dropping database partitions, deleting user accounts, or sending external emails to external clients.

The Two-Phase Approval Workflow

When Claude outputs a tool_use for a Tier 3 action, the agent orchestrator halts execution and follows a two-phase workflow:

  1. Execution Interception & State Serialization: The agent loop intercepts the pending tool_use block. The entire conversation history, pending tool parameters, and execution state are serialized and stored durably (e.g., in a Redis or PostgreSQL task table) with a status of pending_human_approval.
  2. Structured Preview Rendering: The application renders a clear, non-technical confirmation preview to an authorized human operator (e.g., displaying transfer amount, recipient account, fees, and source funds).
  3. Resumption or Rejection Injection:
    • If Approved: The human operator signs off via an authenticated dashboard. The tool handler executes the transaction in production, obtains the real output, and appends a tool_result content block with is_error: false to resume the agent loop.
    • If Rejected: The human operator enters a rejection reason (e.g., "Wire amount exceeds daily threshold of $10,000"). The application appends a tool_result content block with is_error: true containing the supervisor's explanation. Claude ingests this rejection context, acknowledges the decision, and adjusts its plan without crashing.

Execution Sandboxing for Code & Shell Tools

When an application permits Claude to write and execute code (such as executing Python for mathematical modeling, data visualization, or bash scripts for devops automation), the execution environment presents an extreme security hazard. If run on a standard host server, untrusted code can read environment variables, steal cloud credentials, mount cryptominers, or pivot across private VPC subnets.

Sandboxing Technologies: MicroVMs vs. Hardened Containers

FeatureStandard Docker ContainerHardened Container (gVisor runsc)MicroVM (AWS Firecracker)
Isolation LevelShared host Linux kernelUser-space virtualized kernelDedicated hardware-assisted KVM hypervisor
Startup LatencyFast (~500ms)Fast (~100-300ms)Ultra-fast (~5-50ms)
Kernel Attack SurfaceHigh (Direct host syscalls)Low (Syscalls intercepted in user-space)Virtually Zero (Guest kernel isolated from host)
Security GradeInadequate for untrusted codeExcellent for multi-tenant microservicesGold Standard for multi-tenant code execution

Mandatory Sandbox Hardening Checklist

To achieve production security compliance for LLM code execution tools, the runtime sandbox must enforce the following controls:

  1. Read-Only Root Filesystem: Mount the root filesystem as read-only (--read-only), providing only an ephemeral in-memory tmpfs volume for scratch data that is wiped immediately upon process termination.
  2. Non-Root Execution: Execute all processes under a strictly unprivileged user account (UID 65534 / nobody). Never run code under root.
  3. Dropping Linux Capabilities: Drop all default Linux kernel capabilities (--cap-drop=ALL). Specifically eliminate CAP_NET_RAW (packet sniffing) and CAP_SYS_ADMIN (namespace modification).
  4. Strict CGroup Ceilings: Configure Linux Control Groups (cgroups) to enforce hard ceilings on memory (e.g., 512 MB), CPU shares (e.g., 0.5 vCPU), process forks (pids.max = 30 to prevent fork bombs), and disk write quotas.
  5. Network Egress Isolation & IMDS Blocking: By default, disable network egress entirely (--network none). If package installation or web fetching is strictly necessary, route traffic through an egress filtering proxy and strictly block IP address 169.254.169.254.

Critical Exam Concept: The Cloud Metadata Service (169.254.169.254) In cloud environments (AWS, GCP, Azure), the link-local IP 169.254.169.254 hosts the Instance Metadata Service (IMDS). Any code running on an instance that reaches this endpoint can extract temporary IAM credentials assigned to the host VM instance profile. Blocking egress to 169.254.169.254 via iptables or security groups is an absolute prerequisite for LLM code sandboxes.


Comparative Matrix: Tool Safety Tiers and Enforcement

Tool Safety TierTarget OperationsExecution BoundaryAuthorization MechanismSandboxing & Approval Controls
Tier 1: Read-Only QueryDatabase lookup, documentation searchRead-only DB replica, cached search indexServer-side session verificationAutomatic execution; pagination & rate limits
Tier 2: Low-Risk MutationUpdate user preferences, tag ticketsProduction database (write replica)RBAC ownership checks on target resourceAutomatic execution; audit logging & idempotency
Tier 3: High-Risk ActionWire transfers, account deletion, outbound SMSProduction transactional APICryptographic session check + HITL gateMandatory blocking HITL approval; durable state
Tier 4: Dynamic Code ExecPython data analysis, bash scriptingEphemeral MicroVM (Firecracker) or gVisorServer authorization + AST validationRead-only root, no network egress, blocked IMDS

Production Implementation: Secure Tool Handler Pattern

The following Python example illustrates a production-grade tool execution handler that implements server-side session authorization, parameterized queries, and risk-tier validation:

from typing import Dict, Any
import psycopg2

class SecureToolHandler:
    def __init__(self, db_connection_pool):
        self.pool = db_connection_pool

    def execute_tool(
        self,
        tool_name: str,
        tool_input: Dict[str, Any],
        session_context: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Central dispatcher enforcing server-side authorization.
        Never trusts the LLM's invocation decision or identity parameters.
        """
        authenticated_user_id = session_context.get("user_id")
        user_roles = session_context.get("roles", [])

        if not authenticated_user_id:
            return {"is_error": True, "content": "Unauthorized: Missing session authentication."}

        if tool_name == "fetch_user_profile":
            # Tier 1: Read-only query scoped strictly to session user
            return self._handle_fetch_profile(authenticated_user_id)

        elif tool_name == "execute_fund_transfer":
            # Tier 3: High-risk action requiring HITL approval & specific role
            if "financial_admin" not in user_roles:
                return {"is_error": True, "content": "Forbidden: User lacks financial_admin role."}
            return self._stage_fund_transfer_for_hitl(tool_input, authenticated_user_id)

        return {"is_error": True, "content": f"Unknown tool: {tool_name}"}

    def _handle_fetch_profile(self, user_id: str) -> Dict[str, Any]:
        with self.pool.getconn() as conn:
            with conn.cursor() as cur:
                # Parameterized query: immune to SQL injection and IDOR
                cur.execute(
                    "SELECT username, email, tier FROM user_profiles WHERE id = %s",
                    (user_id,)
                )
                row = cur.fetchone()
                if not row:
                    return {"is_error": True, "content": "Profile not found."}
                return {
                    "is_error": False,
                    "content": {"username": row[0], "email": row[1], "tier": row[2]}
                }

    def _stage_fund_transfer_for_hitl(self, tool_input: Dict[str, Any], user_id: str) -> Dict[str, Any]:
        # Instead of transferring funds, stage the record and pause execution
        transfer_id = db_stage_pending_transfer(
            requester_id=user_id,
            destination=tool_input["destination_account"],
            amount=tool_input["amount"]
        )
        return {
            "is_error": False,
            "content": f"Transfer staged with ID {transfer_id}. Awaiting supervisor sign-off."
        }

Common Security Traps on the CCDV-F Exam

  1. The Model-as-Authorizer Trap: Believing that because Claude's system prompt specifies "Only administrators are permitted to call the delete_user tool", the backend can safely execute the tool whenever Claude calls it. Attackers can bypass prompt-level restrictions via injection. Tool handlers must independently verify caller permissions server-side.
  2. The LLM Identity Parameter Trap: Exposing a user_id parameter inside a tool schema and passing it directly to database queries. An attacker can instruct Claude to specify a victim's user_id, accessing private data. Always extract the user identity from the server-side session.
  3. The Unrestricted Docker Trap: Running LLM code execution tools inside standard Docker containers with mounted host Docker sockets (/var/run/docker.sock). Anyone inside the container can talk to the Docker daemon and instantly gain full root control of the host machine.
  4. The Unfiltered Egress Trap: Permitting sandboxed code environments to communicate freely with the local network or cloud provider instance metadata service (169.254.169.254), allowing the extraction of host instance IAM role keys.
Loading diagram...
Zero-Trust Tool Execution, HITL Gate, and Sandboxed Code Isolation
Test Your Knowledge

A developer implements a customer service agent with access to a tool named 'update_account_tier(target_user_id, new_tier)'. When implementing the tool execution handler on the server, which authorization approach strictly adheres to the Zero-Trust Tool Principle?

A
B
C
D
Test Your Knowledge

An autonomous operations agent is granted access to tools that can restart virtual machines, modify DNS routing records, and delete cloud storage buckets. Which mechanism best prevents accidental or malicious catastrophic infrastructure damage during agent execution?

A
B
C
D
Test Your Knowledge

A data analytics platform enables Claude to generate and execute arbitrary Python scripts to manipulate datasets and produce charts. Which sandboxing architecture provides the most robust defense against host filesystem compromise, container breakout, and credential theft?

A
B
C
D