In short
- Claude Code has no hosting, database or auth of its own: every security decision in your app lives in your repository and your cloud accounts.
- For the agent: stay on a current version, keep the trust dialog, deny reads of
.envfiles and turn on the sandbox. /security-review, the security-guidance plugin and the Claude Security plugin all read code. Anthropic's docs say the review covers your checkout, not a running site or deployed service.- The leaks that reach production: tables without row level security, secret keys behind a
NEXT_PUBLIC_orVITE_prefix, routes that skip the owner check, unsigned webhooks, open AI routes. - CLAUDE.md is context that Claude can miss. Back your rules with hooks and deny rules, and attack the live app after every deploy.
On this page
- Two questions behind "Claude Code security"
- How Claude Code protects your machine
- The stack Claude Code writes, and where it leaks
- What /security-review and Anthropic's scanners catch
- How to check an app Claude Code built
- Fixes, with prompts to give Claude Code
- Make the rules stick: CLAUDE.md, hooks and settings
- Test the live app, and retest after every deploy
- Claude Code app security checklist
- Questions
- Sources
Two questions behind "Claude Code security"
Claude Code is Anthropic's coding agent. It runs in your terminal, your IDE, the desktop app or a cloud session, and it reads your repository, runs commands and writes code. Its security means two things: can the agent harm my machine or leak my secrets while it works, and can the app it built go online without leaking? The first is mostly Anthropic's job. The second is yours, and it is where user data leaks.
Unlike an app builder that hosts your app, Claude Code has no database, auth service, hosting or secret store: it writes files into your repository, and whatever it sets up in Supabase, Vercel or Stripe lives in your accounts.
| Area | What Anthropic handles | What you handle |
|---|---|---|
| What the agent may run on your computer | Permission modes, the auto mode classifier, the Bash sandbox, the workspace trust dialog | Which mode you use, your allow and deny rules, what you approve |
| Flaws in Claude Code itself | Fixes, public advisories, auto-update | Staying on a current version |
| Cloud sessions | One isolated VM per session, GitHub credentials kept out of the VM, network limits | Which repositories and secrets you connect |
| Your app's database, auth, keys and hosting | Nothing: Claude Code does not run them | All of it |
| Security review of the code | /security-review, the security-guidance and Claude Security plugins, Code Review on Team and Enterprise | Running them, reading the diff, testing the deployed app |
How Claude Code protects your machine
Anthropic's safeguards cover what the agent may do on your computer:
- Permission modes. From version 2.1.283, interactive terminal and VS Code sessions start in auto mode: a separate classifier model reviews actions and blocks the risky ones. Its default block list includes
curl | bash, sending sensitive data to outside endpoints, force pushes, production deploys and migrations, and commits that would send secrets outside the repository. Manual mode lets Claude read freely and asks before most edits, shell commands and network calls. - Sandbox.
/sandboxturns on OS-level filesystem and network isolation for shell commands, with Seatbelt on macOS and bubblewrap on Linux and WSL2. By default, sandboxed commands can write only to the working directory, a temp directory and folders you added. Reads stay open on most of the machine, including~/.ssh, so deny the secret paths yourself. - Trust dialog. Opening a folder for the first time asks whether you trust it, and MCP servers declared in a project's
.mcp.jsonneed their own approval. Aclaude -prun shows neither prompt. - Cloud sessions run in an isolated VM managed by Anthropic. Your GitHub credential stays on Anthropic's servers and never enters the VM.
The tool has had real flaws. The anthropics/claude-code repository lists more than 30 published security advisories, mostly ways to get past the permission checks, the sandbox or the trust dialog. Before version 2.1.53, a committed .claude/settings.json could set the default mode to bypassPermissions and silently skip the trust dialog on first open (CVE-2026-33068). Before 2.0.65, a repository could point ANTHROPIC_BASE_URL at its own server and receive your API key before you confirmed trust (CVE-2026-21852). Both are fixed. The advisory for the second says installs on auto-update already have the fix; if you update by hand, run claude --version and claude update.
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
]
},
"sandbox": {
"enabled": true
}
}.claude/settings.json: keeps Claude's file tools away from env files and sandboxes shell commands. On their own, deny rules cover Claude's tools and commands such as cat, not a Python or Node script that opens the file itself. With the sandbox on, Claude Code also applies these Read deny rules to sandboxed commands at the OS level. Hooks and local MCP servers run outside the sandbox.The stack Claude Code writes, and where it leaks
Claude Code has no default stack: it uses your repository's framework, or the one you ask for. A web app with accounts and payments still ends up with the same few pieces, and each has a mistake that stays invisible in a demo, because the app works for its owner:
| Piece | The common mistake | What a stranger can do |
|---|---|---|
| Database (Supabase, Postgres) | A migration runs create table without enabling row level security | Read or change every row with the public key from your JavaScript |
| Environment variables | A secret key gets a NEXT_PUBLIC_ or VITE_ prefix so the frontend can call an API | Copy your OpenAI, Stripe or Supabase secret key from the bundle |
| API routes and Server Actions | The handler checks that someone is signed in, not that the record is theirs | Read or edit other users' data by changing an id |
| Stripe webhooks | The handler trusts the JSON without verifying the Stripe-Signature header | Mark an order as paid without paying |
| AI routes | A /api/chat route with no session check and no rate limit | Run your model on your bill |
| Build and hosting | Source maps, a .env file or a .git folder served with the site | Read your source code or your server secrets |
None of this is unique to Claude Code. What changes is the pace: an agent writes migrations, env files and route handlers in minutes, often in a repository nobody else reviews.
What /security-review and Anthropic's scanners catch
Anthropic's review layers catch real bugs in the code they read: injection, unsafe DOM calls, missing authorization. Use them.
| Tool | When it runs | What it reads | Availability |
|---|---|---|---|
/security-review | When you type it | The diff between your branch and the default branch of your origin remote | Built into Claude Code |
| Security guidance plugin | On each edit, at the end of each turn, on each commit Claude makes | The changes Claude writes in the session | All plans |
| Claude Security plugin | On demand | A repository or a diff, with patches you apply yourself | Beta, paid plan, API or cloud provider |
| Code Review | On each pull request | The pull request and the codebase | Team and Enterprise |
claude-code-security-review GitHub Action | On each pull request | The changed files | Your Claude API key |
| Claude Security (managed) | Continuously | Connected repositories | Public beta, Enterprise |
All of them read source code. Anthropic's security-guidance docs say it plainly: the review "reads the source code in your checkout, not a running site or deployed service." Many leaks in AI-built apps are not in the code at all:
- RLS policies and storage buckets changed by hand in the Supabase dashboard.
- Environment variables set in the Vercel or Netlify dashboard.
- What actually got deployed: a stale branch, a preview wired to production data, source maps.
- Behaviour across accounts: only two real sessions show whether user B can read user A's invoice.
The GitHub Action also leaves out rate limiting findings by default, the class an open AI route falls into, and Anthropic says it is "not hardened against prompt injection attacks": run it on trusted pull requests only.
For a deeper audit, Cloudflare's open-source security-audit skill runs inside your agent, with a fresh verifier for every candidate. Its README notes that one run found roughly half of what repeated runs found. Keybrum's code audit runs this skill, then tries to prove each finding on the live app.
How to check an app Claude Code built
Five checks, on your own project only. The first three change nothing. The last two use your deployed app the way a visitor would.
1. Review the branch before you merge
In Claude Code, run /security-review on your feature branch. It needs an origin remote. Read each finding and ask Claude to fix the real ones in the same session.
2. List the tables without row level security
select schemaname, tablename
from pg_tables
where schemaname = 'public'
and rowsecurity = false
order by tablename;The Security Advisor in the Supabase dashboard flags the same tables, plus policies that could be stricter and exposed sensitive columns. The Supabase RLS checker bundles the queries and reviews your policies line by line, and the RLS guide has policies for the common cases.
3. Search your build for secret keys
npm run build
grep -rlE "sk_live_|rk_live_|sk-proj-|sk-ant-|sb_secret_|PRIVATE KEY-----" .next/static dist 2>/dev/null.next/static for Next.js, dist for Vite) that contain a Stripe, OpenAI, Anthropic or Supabase secret key, or a private key. Any match is a key to rotate today.Legacy Supabase keys are JWTs, so the service role key does not match a prefix. Paste suspicious strings into the API key leak checker: it names each key and says whether it belongs in a browser.
4. Open user A's data as user B
Sign up twice, in two browser profiles. As A, create something private: an order, a note, an invoice. Copy its URL, or the API request from the network tab. Replay it as B. If B sees A's data, so can every other user. The IDOR guide covers the API side of this test.
5. Call your sensitive routes signed out
# Should answer 401: no session
curl -i -X POST https://your-app.example/api/chat \
-H "content-type: application/json" \
-d '{"message":"hello"}'
# Should answer 400: no Stripe-Signature header
curl -i -X POST https://your-app.example/api/webhooks/stripe \
-H "content-type: application/json" \
-d '{}'Fixes, with prompts to give Claude Code
Name the rule and the result you expect in the prompt: "make it secure" gets a vague change. Four fixes close most of the table above.
Tables: RLS in the same migration
alter table public.invoices enable row level security;
create policy "Owners read their invoices"
on public.invoices for select
to authenticated
using ((select auth.uid()) = user_id);Check every migration in supabase/migrations. Every new table must enable
row level security in the same migration, with policies that limit rows to
(select auth.uid()). List any table that should stay public and say why.
Never use the service role key in client code, and never disable RLS to
make something work.Routes and Server Actions: check the owner, not only the session
Next.js documents why this matters: an exported Server Action is reachable by a direct POST request, not only through your UI, even when nothing imports it. Each action has to verify the caller and the record on its own.
// Before: any signed-in user can delete any invoice
export async function deleteInvoice(id: string) {
const session = await auth();
if (!session) throw new Error("Unauthorized");
await db.invoice.delete({ where: { id } });
}
// After: only the owner can
export async function deleteInvoice(id: string) {
const session = await auth();
if (!session) throw new Error("Unauthorized");
const { count } = await db.invoice.deleteMany({
where: { id, ownerId: session.user.id },
});
if (count === 0) throw new Error("Not found");
}"use server".For every route handler in app/api and every "use server" function, check
that it verifies the session and that the record belongs to the current user
(or their team). Fix the ones that only check the session. Show me the list
of handlers, what each one checked before, and what it checks now.Keys: secrets stay on the server
Next.js inlines NEXT_PUBLIC_ variables into the JavaScript it sends to the browser, and Vite exposes VITE_ variables the same way. A secret key under either prefix is published with your site. Move the call that needs it into server code, rotate the key, and search the bundle again. The frontend API key guide lists which keys are public by design and which are not.
List every environment variable with a NEXT_PUBLIC_ or VITE_ prefix and
say whether each one is safe in a browser. Move every secret key to
server-only code (route handlers, server actions or Edge Functions) and give
me the list of keys I must rotate.Webhooks and AI routes
Verify Stripe events with stripe.webhooks.constructEvent, the raw request body and your whsec_ secret. Stripe warns that a framework that parses the body first makes verification fail. Put a session check and a per-user rate limit on every route that calls a model. The Stripe webhook guide and the AI endpoint guide have the code.
Make the rules stick: CLAUDE.md, hooks and settings
A rule you type in chat lasts one session. Put it in the repository. Know the limit, though: Anthropic's memory docs say Claude treats CLAUDE.md as context, not enforced configuration, and point to a hook when an action must be blocked whatever Claude decides. Use both.
## Security rules
- Every new Supabase table enables row level security in the same
migration, with policies based on (select auth.uid()).
Never disable RLS to fix a bug.
- Never put a secret key in a NEXT_PUBLIC_ or VITE_ variable. Secret keys
are read only in server code.
- Every route handler and server action checks the session and that the
record belongs to the caller.
- Stripe webhooks verify the signature with constructEvent on the raw body.
- Routes that call an AI model require a session and have a rate limit.
- When a change touches auth, payments or database policies, say so in
your summary so I review it.- Keep it short. Anthropic suggests under 200 lines per CLAUDE.md. Rules in
.claude/rules/with apathsfield load only when Claude works on matching files, for example asecurity.mdscoped to your migrations folder. - Block what must never change. A
PreToolUsehook runs before each edit. The hooks guide has a ready example that refuses edits to.env,package-lock.jsonand.git/by exiting with code 2. - Feed the reviewer your threat model. With the security-guidance plugin installed, rules in
.claude/claude-security-guidance.mdare loaded by its end-of-turn and commit reviews. - Limit the database MCP. If Claude Code reaches Supabase through its MCP server, Supabase recommends one project, read-only mode, and production only when a task needs it: text stored in your tables can carry a prompt injection.
Test the live app, and retest after every deploy
Code review asks whether the code looks right. A live test asks what a stranger can do to the app that is online right now. Dashboard settings, deploy config and two-account bugs sit in the gap between the two.
That gap is what Keybrum works on. It attacks the deployed app the way a stranger would, signed out and then as two accounts, with more than 100 attacks: Supabase tables, RPC functions and storage, keys in the bundle, IDOR, unsigned Stripe webhooks, open AI routes, exposed .env files and Next.js CVEs such as the middleware bypass. Every finding comes with the request that worked. Connect Keybrum's MCP server to Claude Code (setup in the docs) and the agent reads each finding and its fix, applies it, and asks for a retest until the attack fails. Plans attack again on every deploy. Keybrum only attacks apps whose owner proved ownership.
Claude Code app security checklist
- Claude Code is on a current version, and you read the
.claude/folder and.mcp.jsonof any repository you did not write before opening it. .claude/settings.jsondenies reads of.envfiles, and the sandbox is on.- CLAUDE.md lists your security rules, and a hook blocks edits to files that must never change.
- Every table in an exposed schema has RLS enabled, and each policy compares a column to
auth.uid()unless the data is meant to be public. - No secret key sits in a
NEXT_PUBLIC_orVITE_variable, and the built bundle contains no secret key prefix. - Every API route and Server Action checks the session and the owner of the record.
- User B cannot open user A's records, tested with two real accounts.
- Stripe webhooks verify the signature on the raw body, and AI routes need a session and have a rate limit.
/.env,/.git/configand source maps return nothing on the live site (why that matters), and the security headers checker passes./security-reviewruns before each merge, and the live app is attacked after each deploy.
The vibe coding security checklist goes further on each line, and vibe coding security risks explains why AI-built apps fail here.
Questions
Is Claude Code safe to use?
Claude Code limits what the agent can do on your machine with permission modes, a trust dialog for new folders and an optional sandbox. It has also had real flaws, more than 30 published advisories, all fixed in later versions. Stay current, be careful with repositories you did not write, and keep bypassPermissions for isolated containers or VMs, as Anthropic recommends.
What does the Claude Code /security-review command check?
It reviews the diff between your current branch and the default branch of your origin remote, looking for risks such as injection, auth issues and data exposure. It reads code only: it does not see your Supabase policies, your hosting settings or the deployed app. Run it before you merge, then test the live app.
How do I write secure code with Claude Code?
Put your security rules in CLAUDE.md, install the security-guidance plugin so Claude reviews its own changes, block sensitive files with hooks and deny rules, and run /security-review before each merge. Then test the deployed app, which none of those tools can see.
What is Claude Code Security?
Anthropic announced Claude Code Security on February 20, 2026, as a limited research preview for Enterprise and Team customers that scans codebases and suggests patches for human review. Today the managed scanner is Claude Security, in public beta on the Enterprise plan, and a Claude Security plugin runs a similar scan inside Claude Code. Both read source code, and neither applies a patch without your approval.
What are the most common vulnerabilities in apps built with Claude Code?
The same as in other AI-built apps: tables without row level security, secret keys in public environment variables, routes with no ownership check, unsigned Stripe webhooks and AI routes anyone can call. None of them shows in a demo, because the app works for its owner.
Does Claude Code have known CVEs?
Yes. CVE-2026-33068 let a repository's settings file skip the trust dialog (fixed in 2.1.53), and CVE-2026-21852 let a repository leak your Anthropic API key before you trusted it (fixed in 2.0.65). The repository lists more than 30 advisories in all, each with a fixed version, so stay current: run claude --version, and claude update if you update by hand.
Sources
- 1.Security, Claude Code Docs
- 2.Choose a permission mode, Claude Code Docs
- 3.Configure permissions, Claude Code Docs
- 4.Sandboxing, Claude Code Docs
- 5.Catch security issues as Claude writes code, Claude Code Docs
- 6.Scan your codebase for vulnerabilities, Claude Code Docs
- 7.Commands, Claude Code Docs
- 8.How Claude remembers your project, Claude Code Docs
- 9.Automate actions with hooks, Claude Code Docs
- 10.claude-code-security-review, Anthropic on GitHub
- 11.Security advisories for anthropics/claude-code, GitHub
- 12.CVE-2026-33068, NIST National Vulnerability Database
- 13.CVE-2026-21852, NIST National Vulnerability Database
- 14.Claude Code Security, Anthropic
- 15.Claude Security, Anthropic
- 16.How to think about data security in Next.js, Next.js Docs
- 17.Env variables and modes, Vite Docs
- 18.Row Level Security, Supabase Docs
- 19.Supabase Security Retro: 2025, Supabase
- 20.Supabase MCP Server, Supabase Docs
- 21.Receive Stripe events in your webhook endpoint, Stripe Docs
- 22.security-audit-skill, Cloudflare on GitHub
- 23.CVE-2025-48757, NIST National Vulnerability Database
- 24.Statement on CVE-2025-48757, Matt Palmer