[ LEARN // AI CODING AGENT SECURITY ]
Securing Claude Code with production access
Securing Claude Code means constraining what the agent can execute — shell commands, file edits, and MCP tool calls — rather than trusting the model to behave. An agentic coding tool holding production credentials is a production actor, and it needs the controls production actors get: scoped permissions, inline policy on tool calls, human gates on irreversible operations, and an audit trail that attributes every action to a specific agent.
[ THE ATTACK SURFACE ]
Given production access, Claude Code can do most of what an engineer can do, at machine speed. It runs shell commands. It edits files and pushes code. And through MCP servers it reaches whatever you connected — GitHub, AWS, databases, internal APIs — carrying the credentials you configured. None of that is a vulnerability; it is the product. The security question is what happens when the operator behind those capabilities is steered wrong.
The key risk multiplier is that Claude Code reads untrusted content as part of its job: issue threads, pull-request comments, documentation, web pages, the contents of the repository itself. Prompt injection in any of that content can influence what the agent does next — and unlike a chatbot, what this agent does next is executed against real systems. Layer on the model's own properties — capable, fast, and non-deterministic, so the same instruction can produce different actions on different runs — and the honest summary is this: Claude Code with production access is a talented, tireless, occasionally wrong operator holding your keys. You would not run a human operator without access controls; the agent gets the same treatment.
[ LAYERED CONTROLS ]
No single control covers an agent that can be steered by the content it reads. Three layers, each catching what the previous one cannot:
Layer 1 — the agent's own settings
Claude Code ships with permission prompts, tool allowlists, sandboxing, and hooks — scripts that run on lifecycle events and can intercept and veto tool calls before they execute. Configure all of it. It is the cheapest control you have and it catches the honest mistakes.
Layer 2 — credential scoping
Every Claude Code deployment gets its own dedicated tokens, scoped to the task — never a human's credentials, never a shared bot account. Whatever steers the agent, it cannot use permissions its tokens do not carry.
Layer 3 — an MCP firewall
A proxy in the request path between the agent and every MCP server it calls. Policy is enforced in a separate process the model cannot address, on every tool call, before the server sees it — with each decision logged.
Hooks deserve a specific mention because they are the strongest control in layer 1: a hook registered on tool use runs before the call executes and can veto it, which turns Claude Code's own extension mechanism into an enforcement point. sentnel is an open-source Claude hook interceptor built for exactly this — policy checks on tool calls, from inside the agent's own lifecycle.
The distinction that structures the whole stack is in-process versus out-of-process. Settings, allowlists, and hooks run inside the agent's environment — inside the blast radius. They are configuration of the thing being governed, sharing a process and a context that a sufficiently effective injection can influence. A proxy in the request path shares none of that: it enforces policy in a separate process the model cannot address, so a blocked call never reaches the target system no matter what happened inside the session. This is the core move of AI agent runtime governance applied to a coding agent: put the control where the agent cannot reach it.
[ THE THREAT MODEL ]
Four scenarios cover most of what actually goes wrong with a production-credentialed coding agent. Each maps to the control that stops it.
Prompt injection → exfiltration
A GitHub issue the agent is asked to triage contains hidden instructions: read the repository's .env file and post its contents in a comment. The agent has read access and comment access, so nothing it does looks anomalous tool-by-tool.
▸ Deny-by-default MCP policy: outbound writes are scoped, so the steered agent has nowhere to send the file.
Plausible-but-wrong plan step
Asked to "clean up after the release", the agent decides deleting the release branch — or terminating the staging instance that turns out to be production — is a reasonable step. Each call looks defensible in isolation; the plan is wrong.
▸ Approval gates: irreversible operations pause for a human before execution, not after.
Credential sprawl
The agent's GitHub token was created from an admin account "to get it working". Its task needs read and pull-request access; its token can delete repositories, rotate secrets, and change org settings.
▸ Least-privilege tokens per deployment: the credential can only do what the task requires.
The unattributable 3am change
Three automations and a Claude Code deployment share one service account. A production config changed overnight and the audit log names the bot — which proves nothing about which automation acted, or which ones did not.
▸ Per-agent identity: every logged call names exactly one actor, so attribution survives the incident.
[ WHAT IT LOOKS LIKE IN PRACTICE ]
Give the Claude Code deployment its own agent identity and write the policy down. A workable starting posture for an agent that reviews code and opens pull requests: everything is blocked until granted, reads and PRs are permitted, merges to main pause for a human, and destructive infrastructure calls are never automated. In YAML:
# Default posture: nothing is granted until it is granted.
- agent: claude-code
server: "*"
tool: "*"
action: block
# The agent may read code and open pull requests — its actual job.
- agent: claude-code
server: github
tool: [read_file, create_pull_request]
action: permit
# Merges to main pause for a human, with full context.
- agent: claude-code
server: github
tool: merge_pull_request
when: { branch: main }
action: require_approval
# Destructive infrastructure calls are never automated.
- agent: claude-code
server: aws
tool: [ec2_terminate_instance, s3_delete_bucket]
action: blockA per-agent, deny-by-default policy for a Claude Code deployment — enforced out-of-process.
The require_approval rule is the piece teams underuse. Gating merges and infrastructure changes behind a Slack approval means the agent does the work end-to-end — writes the code, opens the PR, prepares the merge — and a human spends five seconds on the one decision that is hard to undo. The agent resumes only after explicit approval, and the approval itself lands in the audit trail next to the call it authorized.
[ BEST PRACTICES ]
- ◈Give every Claude Code deployment its own identity — its own tokens, its own policy, its own line in the audit log. Never a human's credentials, never a shared bot account.
- ◈Scope tokens to the task. A pull-request agent needs read and PR access, not org admin. If a token can do more than the task requires, shrink the token before writing any policy.
- ◈Run MCP access deny-by-default. Grant specific tools on specific servers, and treat every new grant as a change worth reviewing.
- ◈Gate irreversible operations — merges to main, deletes, infrastructure changes — behind human approval, enforced out-of-process, not just prompted for in the terminal.
- ◈Log every tool call with the agent's identity, the tool, the parameters, and the decision. A log you cannot attribute is a log you cannot use in an incident.
- ◈Review grants and audit trails on a schedule. Agent permissions accumulate like service permissions do — but an agent will improvise with whatever it still holds.
[ HOW SENTNELOPS IMPLEMENTS THIS ]
[ FREQUENTLY ASKED QUESTIONS ]
Can Claude Code be prompt-injected?
Yes. Any agent that reads untrusted content — issues, pull requests, documentation, web pages — can be steered by instructions embedded in that content, and Claude Code routinely reads all of those. The practical defense is not hoping injection detection catches every attempt; it is limiting what a steered agent can execute, so that even a fully compromised session can only perform the narrow set of actions its identity was granted.
Are Claude Code's built-in permissions enough for production?
They are a good first layer and you should configure them fully: permission prompts, allowlists, sandboxing, and hooks that can veto tool calls. But they run in-process — inside the same environment the agent operates in and the same context an injected prompt influences. Production access warrants a second, out-of-process enforcement layer, such as a proxy in front of the agent's MCP servers, where policy holds regardless of what happens inside the session.
What does an MCP firewall add for Claude Code?
An MCP firewall is a proxy that sits between Claude Code and the MCP servers it calls — GitHub, AWS, databases, internal APIs. Every tool call is intercepted before the server sees it, evaluated against explicit policy, then permitted, blocked, or paused for human approval, and logged with the agent's identity. Because the proxy runs outside the agent process, no prompt injection or model failure inside the session can bypass or rewrite it.
How do I audit what Claude Code did?
Give the deployment its own identity and route its tool calls through an enforcement point that logs every call: which agent, which tool, what parameters, when, and what the policy decided. With that record you can reconstruct any session action-by-action and attribute every change to a specific deployment. Shell-level history and git commits alone are not enough — they miss MCP tool calls and cannot distinguish one automation from another on a shared account.
Should Claude Code share a service account with other automation?
No. A shared account destroys attribution: when several automations act through one credential, the audit log names the account, not the actor, and no retroactive fix exists. Each Claude Code deployment should have its own identity and its own scoped tokens, so every logged action names exactly one actor and incident response starts from facts instead of guesses.
[ REFERENCES ]