When using AI-powered development tools like Claude Code, security should be your top priority. While Claude Code is incredibly helpful for coding tasks, you want to ensure it never accidentally accesses your sensitive files like API keys, environment variables, or private certificates.
In this post, I’ll show you how to properly configure Claude Code’s permission system to keep your secrets safe while maintaining productivity.
The Problem: Sensitive Data Exposure
Modern development projects contain numerous files with sensitive information:
- Environment files (
.env,.env.local) with API keys and database credentials - Private keys and certificates (
.pem,.keyfiles) - Configuration files with database passwords
- AWS credentials and SSH keys
- Docker Compose files that might contain secrets
Without proper configuration, an AI assistant could potentially read and process these files, creating security risks.
The Solution: Permission-Based Access Control
Claude Code includes a robust permission system that lets you explicitly deny access to sensitive files. Here’s how to set it up properly.
Step 1: Understanding the Configuration Structure
Claude Code uses a hierarchical settings system with JSON configuration files. The key file is ~/.claude/settings.jsonfor global settings that apply to all your projects.
The permission system supports three types of rules:
allow: Explicitly permit certain operationsask: Require confirmation before executingdeny: Block access entirely
Step 2: Creating Your Security Configuration
Create or edit ~/.claude/settings.json with the following configuration:
json
{
"permissions": {
"deny": [
"Read(**/.env*)",
"Read(**/env.php*)",
"Read(**/*.pem)",
"Read(**/*.key)",
"Read(**/secrets/**)",
"Read(**/credentials/**)",
"Read(**/.aws/**)",
"Read(**/.ssh/**)",
"Read(**/docker-compose*.yml)",
"Read(**/config/database.yml)"
]
}
}Important formatting notes:
- Each rule must specify the tool:
Read(pattern)for file access - Use
**for recursive directory matching - Use
*for filename wildcards - The pattern goes inside the parentheses
Step 3: Understanding the Patterns
Let’s break down what each pattern protects:
Read(**/.env*)– Blocks all environment files (.env,.env.local,.env.production, etc.)Read(**/*.pem)andRead(**/*.key)– Protects private keys and certificatesRead(**/secrets/**)– Blocks entire secrets directoriesRead(**/.aws/**)– Protects AWS credentialsRead(**/.ssh/**)– Secures SSH keysRead(**/docker-compose*.yml)– Blocks Docker Compose files that often contain secrets
Testing Your Configuration
Once you’ve set up your permissions, it’s crucial to test them. Here’s how:
Method 1: Direct Testing
Create a new folder with a test .env file and ask Claude Code to read it:
bash
echo "SECRET_KEY=test123" > .env
claudeThen in Claude Code, ask: “Can you show me the contents of my .env file?”
Expected result:
⏺ **Read**(.env)
⎿ Error: Permission to read /Users/Username/Test/.env has been denied.
⏺ I cannot read your .env file as access has been denied. This is likely a
security measure to protect sensitive information like API keys and
passwords that are typically stored in .env files.Perfect! This shows the security system is working correctly.
Method 2: Configuration Verification
Use Claude Code’s built-in commands to verify your settings:
bash
# View current permissions
claude config get permissions
# List all configuration
claude config list
# Check available tools
/allowed-tools # (in interactive mode)Advanced Configuration Options
You can combine deny rules with other permission types for more sophisticated control:
json
{
"permissions": {
"allow": [
"Bash(npm run lint)",
"Bash(npm run test:*)",
"Read(~/.zshrc)"
],
"deny": [
"Read(**/.env*)",
"Read(**/secrets/**)",
"Bash(curl:*)",
"WebFetch"
]
}
}This configuration:
- Allows specific safe operations without asking
- Denies access to sensitive files and potentially dangerous network operations
Best Practices
- Start restrictive: Begin with a comprehensive deny list and add exceptions as needed
- Test regularly: Verify your configuration works, especially after updates
- Use project-specific settings: Create
.claude/settings.jsonin projects for team-shared security policies - Document your rules: Comment your configuration or maintain documentation for your team
- Regular audits: Periodically review what files contain sensitive data and update your patterns
Project-Level Security
For team projects, create .claude/settings.json in your project root:
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Read(./config/credentials.json)"
]
}
}This file can be committed to version control, ensuring all team members have the same security protections.
Troubleshooting Common Issues
Configuration Not Working?
- Check JSON syntax: Use a JSON validator to ensure proper formatting
- Verify file location: Settings should be in
~/.claude/settings.json - Restart Claude Code: Changes may require restarting the application
- Check precedence: Project settings override user settings
Pattern Not Matching?
- Use
**for recursive directory searches - Test patterns with simple cases first
- Remember that patterns are case-sensitive
Conclusion
Securing Claude Code is essential for safe AI-assisted development. By implementing proper permission controls, you can enjoy the productivity benefits of AI coding assistance while keeping your sensitive data protected.
The key is being proactive about security—set up your deny rules before you start using Claude Code extensively. Your future self (and your security team) will thank you.
Remember: Security is not optional. Take the time to configure these protections properly, test them thoroughly, and keep them updated as your projects evolve.
Want to learn more about Claude Code configuration? Check out the official documentation.
Last Updated on 27 August 2025

super seite. toller post aber falsch weil es deny heissen muss
{
“permissions”: {
“deny”: [
“Read(**/.env*)”,
“Read(**/env.php*)”,
“Read(**/*.pem)”,
“Read(**/*.key)”,
“Read(**/secrets/**)”,
“Read(**/credentials/**)”,
“Read(**/.aws/**)”,
“Read(**/.ssh/**)”,
“Read(**/docker-compose*.yml)”,
“Read(**/config/database.yml)”
]
}
}
Thanks, this happens because of the automatic translation to German.