Claude Code Security Best Practices: API Keys, Permissions & Production Protection
Practical Claude Code security for API keys, permissions, secrets, commit scans, and production protection with working examples.
Claude Code has powerful file manipulation and command execution capabilities — but misconfiguration can lead to irreversible accidents. Committing a .env file, accidentally deleting a production database, printing an API key to logs — these are all real incidents caused by using Claude Code without proper safeguards.
This article provides an implementation-level breakdown of security best practices for Claude Code. Rather than theory, the focus is on ready-to-use configuration and preventive code you can copy and apply immediately.
Why Claude Code Needs Security Measures
Unlike a regular text editor, Claude Code has the following capabilities:
- Read, write, and delete any file (
Read/Write/Edit/Bash(rm)) - Execute shell commands (
Bash) - Network access (
WebFetch/ API calls) - Post to external services (Qiita, GitHub, Slack, etc.)
All of these can be executed with user approval. The problem is that mechanically approving every prompt allows unintended operations to slip through. Security measures are about structurally eliminating the room for mistakes.
Measure 1: API Key Management — .env + .gitignore Is the Foundation
What NOT to Do
// ❌ Hardcoded in source code
const client = new Anthropic({ apiKey: "PASTE_REAL_API_KEY_HERE" });
// ❌ Written in CLAUDE.md or config files
// ANTHROPIC_API_KEY=PASTE_REAL_API_KEY_HERE
// ❌ Passed inside a claude -p prompt
// Use QIITA_TOKEN=PASTE_REAL_TOKEN_HERE to post to Qiita
The Right Approach
# .env (excluded from git, stored only on local machine)
ANTHROPIC_API_KEY=<anthropic-api-key>
QIITA_TOKEN=<qiita-token>
SLACK_BOT_TOKEN=<slack-bot-token>
DATABASE_URL=postgresql://...
# Always add to .gitignore
.env
.env.*
.env.local
!.env.example # ← Sample file is OK to commit
*.pem
*.key
credentials.json
*-service-account.json
# .env.example (safe to commit, values left empty)
ANTHROPIC_API_KEY=
QIITA_TOKEN=
SLACK_BOT_TOKEN=
DATABASE_URL=
Reading Variables in Code
// ✅ Read from environment variables
import { config } from "dotenv";
config();
const token = process.env.QIITA_TOKEN;
if (!token) throw new Error("QIITA_TOKEN is not set. Please check your .env file.");
Document Prohibitions in CLAUDE.md
## Security Prohibitions
- Never include API keys or tokens in prompts
- Never read and output the contents of .env files
- Never write environment variable values in logs or comments
- Never console.log the contents of process.env
Measure 2: Pre-Commit Secret Scanning
Even with .env in .gitignore, accidental entries in other files or copy-paste mistakes can still happen. Add an automatic pre-commit scan.
Pre-Commit Check with Hooks
.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash(git commit*)",
"hooks": [
{
"type": "command",
"command": "node scripts/secret-scan.mjs"
}
]
}
]
}
}
scripts/secret-scan.mjs:
import { execSync } from "child_process";
// Get staged changes
const diff = execSync("git diff --cached").toString();
const PATTERNS = [
{ name: "Anthropic API key assignment", re: /\b(?:ANTHROPIC_API_KEY|CLAUDE_API_KEY)\s*[:=]\s*["']?[^"'\s]{20,}/i },
{ name: "OpenAI API key assignment", re: /\bOPENAI_API_KEY\s*[:=]\s*["']?[^"'\s]{20,}/i },
{ name: "AWS Access Key", re: /\bAKIA[0-9A-Z]{16}\b/ },
{ name: "Slack Token", re: /\bxox[baprs]-[0-9A-Za-z-]{10,}\b/ },
{ name: "Generic Secret", re: /\b(?:secret|token|api[_-]?key|password)\s*[:=]\s*["'][^"']{10,}["']/i },
];
const found = PATTERNS.filter(({ re }) => re.test(diff));
if (found.length > 0) {
console.error("🚨 Secret detected! Aborting commit:");
found.forEach(({ name }) => console.error(` - ${name}`));
console.error("\nFix: run `git reset HEAD <file>` to unstage the file");
process.exit(1); // Exit code 1 → Hook blocks the command
}
console.log("✓ Secret scan: no issues found");
process.exit(0);
This means the moment Claude Code tries to run git commit, an automatic scan runs and any detected leaks are blocked.
Measure 3: Permission Mode Configuration
Claude Code’s allow/deny settings can be controlled at a fine-grained level per file.
Permission Settings in .claude/settings.json
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"defaultMode": "default",
"disableBypassPermissionsMode": "disable",
"allow": [
"Read(**)",
"Glob(**)",
"Grep(**)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Bash(rm -rf*)",
"Bash(git push --force*)",
"Bash(git reset --hard*)",
"Bash(DROP TABLE*)",
"Bash(truncate*)",
"Bash(curl * | bash)",
"Bash(wget * | sh)"
],
"ask": [
"Write(**)",
"Edit(**)",
"Bash(git commit*)",
"Bash(git push*)",
"Bash(npm publish*)",
"Bash(wrangler pages deploy*)"
]
},
"disableAutoMode": "disable"
}
| Setting | Meaning |
|---|---|
allow | Execute without confirmation |
deny | Never execute (blocked entirely) |
ask | Requires approval every time |
Key principle: Destructive commands go in deny, write operations in ask, read operations in allow.
Separate Production and Organization Policy with Local or Managed Settings
The official settings model applies scopes in this order: managed, command-line arguments, local, project, then user. Instead of relying on an unofficial custom-file switch, use .claude/settings.local.json for a stricter personal production terminal and managed settings for organization-wide rules.
Use .claude/settings.local.json for local-only overrides and keep it out of git.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"defaultMode": "plan",
"disableBypassPermissionsMode": "disable",
"allow": ["Read(**)", "Glob(**)", "Grep(**)", "Bash(git log*)", "Bash(git diff*)"],
"deny": ["Write(**)", "Edit(**)", "Bash(git push*)", "Bash(rm*)", "Bash(*deploy*)"],
"ask": []
},
"disableAutoMode": "disable"
}
For a team or company policy, put the same boundary in managed settings. With allowManagedPermissionRulesOnly, users and project repositories cannot redefine allow, ask, or deny rules around the organization policy.
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Bash(curl *)"
],
"disableBypassPermissionsMode": "disable"
},
"disableAutoMode": "disable",
"allowManagedPermissionRulesOnly": true
}
Measure 4: Production Environment Protection
Explicitly Separate Connection Targets
## CLAUDE.md — Production Environment Rules
## Environment Detection
- If DATABASE_URL contains 'prod' or 'production', this is a **production environment**
- In production, never execute:
- DROP / TRUNCATE / DELETE (without WHERE clause)
- Migrations (require prior confirmation)
- Bulk file deletions
## Confirmation Flow
All production changes must:
1. Be tested in a staging environment first
2. Receive user confirmation
3. Report results after execution
Control Connections with Environment Variables
// scripts/db-query.mjs
const env = process.env.NODE_ENV ?? "development";
const dbUrl = process.env.DATABASE_URL;
if (env === "production" && process.argv.includes("--write")) {
console.error("❌ Writing to production requires the --force-production flag");
process.exit(1);
}
Measure 5: File Operation Safety Guards
Automate Backups Before Deletion
// Hooks in .claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash(rm *)",
"hooks": [
{
"type": "command",
"command": "echo '⚠️ A delete command is about to run. Press Ctrl+C to cancel.' && sleep 3"
}
]
}
]
}
}
Protect Critical Files from Accidental Edits
## CLAUDE.md — Files That Must Not Be Modified
The following files must **never be edited**:
- .env (contains environment variables and secret keys)
- wrangler.toml (Cloudflare production configuration)
- scripts/deploy.sh (deployment script)
- .github/workflows/*.yml (CI/CD configuration)
If changes are needed, confirm with the user first.
5 Common Pitfalls
1. Adding .gitignore after the fact is too late
A .env file that was already committed remains in git history even after adding it to .gitignore.
# Complete removal from history (caution: requires force push)
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch .env" \
--prune-empty --tag-name-filter cat -- --all
# Alternatively, use BFG Repo Cleaner
If the file was already pushed to GitHub, always rotate your API keys first before addressing the history.
2. Storing service account JSON files in the repository
Service account keys for Google Cloud or AWS are often distributed as .json files, but storing them in a repository is dangerous. Convert them to environment variables or migrate to a Secret Manager (AWS Secrets Manager / GCP Secret Manager).
3. Running interactive commands with the Bash tool
During headless execution with claude -p, commands that require interactive input like sudo or vim will cause the process to hang. Use non-interactive commands only.
4. Including credentials in error messages
// ❌ Dangerous: API key appears in logs
throw new Error(`Authentication failed: token=${process.env.TOKEN}`);
// ✅ Safe: don't expose the value
throw new Error(`Authentication failed: please check the TOKEN environment variable`);
5. Reusing the same permission settings across all projects
Using the same settings.json for personal and work projects means the relaxed settings from your personal project can override the stricter requirements needed for work. Manage .claude/settings.json separately for each project.
Practical Operating Rules
Do Not Expose Secrets During Normal Work
When you ask Claude Code to “fix this error”, do not paste the full log or the contents of .env. Decide up front that API keys, OAuth tokens, cookies, database URLs, customer emails, billing IDs, and private keys are never copied into prompts or issue comments.
If Claude Code needs context, redact values and keep only the shape of the configuration. DATABASE_URL=***REDACTED*** is enough for Claude Code to understand that a database URL exists. The real value is not needed for debugging most application code.
| Safe to Provide | Do Not Provide |
|---|---|
| Error type, stack trace file names, reproduction steps | API keys, private keys, session cookies, real .env values |
.env.example, setting names, permission rules, failed command name | Production database URLs, customer data, internal credentials |
| Redacted logs, test data, local sample values | Real tokens, service account JSON, screenshots containing secrets |
Put this table in CLAUDE.md so every session starts with the same boundary. In teams, “do not expose secrets” should be an operating rule, not tribal knowledge.
Duplicate Secret Scanning Before Commit
The PreToolUse Hook above blocks a commit when Claude Code is the actor. It does not cover a human committing from another terminal or generated files created in CI. In production teams, layer three checks: the Claude Code Hook, a local pre-commit scan, and a CI secret scan that blocks merge.
The minimum useful setup is: block locally with the Claude Code Hook, re-check in CI, and fail the pull request when a secret pattern appears. Tools such as gitleaks or trufflehog are better for CI-scale detection, while the script in this article remains useful as a fast local guard.
Concrete Failure Examples
Failure 1: Reading .env during investigation
A developer asked Claude Code to “check the environment variables”, and real values ended up in conversation or working logs. The fix is to deny reads of .env and provide .env.example only.
Failure 2: Pasting a Qiita publishing token into a prompt
An automation task included QIITA_TOKEN directly in the prompt, leaving a chance that a subagent or log file would persist it. The safer workflow is to keep the token in .env and have commands reference only the environment variable name.
Failure 3: Production and staging database URLs looked similar
The instruction said only “clean up the database” and skipped connection verification. If the production URL was active, a delete or migration could become an incident. Print NODE_ENV, host, and database name before write operations, and require explicit user confirmation.
First Response After an Incident
If you suspect a secret leaked, the first step is not history cleanup. The first step is disabling the credential.
- Rotate or revoke the affected API key, token, or password immediately
- Check GitHub Actions, Cloudflare, AWS, GCP, and SaaS audit logs for use after exposure
- Record who exposed what, when, in which repository, and through which channel
- Remove the secret from git history and logs, then communicate any force-push impact
- Tighten
denyrules, Hooks, CI scans, andCLAUDE.mdprohibitions before resuming work
In practice, stopping the credential first and cleaning history second reduces mistakes. During incident response, people often paste more logs while trying to help, so prepare a redacted-log template before you need it.
Training and Consultation Path
Claude Code security is not solved by one settings file. You also need to review repository layout, CI, deployment permissions, and team approval flow.
At ClaudeCodeLab, the learning path for individual developers starts with .env hygiene and permission rules. For team consultations, the first questions are where managed settings should be enforced and where CI secret scanning should block merges. If Claude Code is already used at work, start by mapping who can access which secrets before tuning individual permissions.
Security Checklist
A checklist for setting up a Claude Code project:
### Basic Setup
- [ ] Created .env and added to .gitignore
- [ ] Created .env.example and shared with team
- [ ] Verified no secrets in existing commits via git log
### Permission Settings
- [ ] Configured deny list in .claude/settings.json
- [ ] Added destructive commands (rm -rf, DROP TABLE, etc.) to deny
- [ ] Set production deploy commands to ask
### Automation
- [ ] Configured pre-commit secret scan Hook
- [ ] Documented security prohibitions in CLAUDE.md
### Operations
- [ ] Established API key rotation schedule (recommended: every 90 days)
- [ ] Production work is restricted with .claude/settings.local.json or managed settings
- [ ] Documented incident response flow
Summary
Claude Code security isn’t about “imposing restrictions” — it’s about building a structure where accidents can’t happen.
| Threat | Countermeasure |
|---|---|
| API key leakage | .env + .gitignore + secret scan Hook |
| Unintended deletion | deny list + pre-deletion Hook |
| Production mistakes | local/managed settings + CLAUDE.md prohibitions |
| Commit contamination | PreToolUse Hook for pre-commit scanning |
Once configured, these settings are essentially maintenance-free. Spending 30 minutes today can prevent a major incident in the future.
Related Articles
- Claude Code Permissions Guide
- Claude Code Security Failure Cases
- Claude Code + SaaS Integration Complete Guide
- CLAUDE.md Best Practices
References
Free PDF: Claude Code Cheatsheet
Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.
We handle your data with care and never send spam.
Level up your Claude Code workflow
Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.
About the Author
Masa
Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.
Related Posts
Claude Code Verification Receipt: Prove AI Changes With Build, Public URL, CTA, and Screenshots
A practical Claude Code verification receipt for diff, build, public URL, CTA, screenshot, and revenue-path checks.
Claude Code Permission Budget Loop: Ship Safely Without Approving Every Command
Design a permission budget for Claude Code so safe work moves fast while secrets, deploys, billing, and data stay protected.
Claude Code Prompt Library Maintenance: Turn One-Off Prompts Into Assets
Name, test, and reuse Claude Code prompts so they become a reliable path from free PDF learning to the paid prompt pack.
Related Products
The Complete Claude Code Setup & Configuration Guide
From install to team-ready workflow.
A practical guide to installation, CLAUDE.md, hooks, MCP servers, permissions, IDE setup, and CI/CD workflows.
50 Battle-Tested Claude Code Prompt Templates
Copy, paste, ship. 50 production-ready prompts.
Use proven prompts for code review, refactoring, testing, documentation, debugging, architecture, and incident response.