Claude Code 2.1.83 shipped a critical security feature: plugin credential management. If you're building or using plugins that connect to external APIs, you need to understand what this does—and what it doesn't do—to use it effectively.
What Changed in 2.1.83
When you install a plugin that declares credential requirements, Claude Code now prompts for those values upfront and stores them in your OS keychain (macOS Keychain or Windows Credential Manager). This replaces the old pattern of storing credentials in plaintext files like ~/.claude/settings.json or project .env files.
The companion feature: CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1. When enabled, this strips credentials from subprocess environments—preventing leaks to bash tools, hooks, and MCP stdio servers.
The immediate win: credentials won't accidentally end up in git commits, get scraped by background processes, or remain on shared machines.
What Keychain Storage Doesn't Do
This is crucial: keychain storage is not a permission sandbox. It securely stores your credential, but doesn't restrict what that credential can do. If you store a full-access Stripe API key in the keychain, you have a full-access key stored securely—not a restricted key.

The real security comes from plugin architecture, not just the storage mechanism.
The Right Way to Build Secure Plugins
A properly architected plugin never exposes the API key to Claude at all. Here's the flow:
- Claude invokes your plugin's tool (like
get_charges) - Plugin reads the key from keychain
- Plugin makes the API call
- Plugin returns structured data
- Claude sees only the data, never the credential
Here's the Python pattern:
import stripe
import keyring
def get_plugin_client():
api_key = keyring.get_password("claude-plugin-stripe", "api_key")
return stripe.StripeClient(api_key)
def get_charges(customer_id: str, limit: int = 10):
client = get_plugin_client()
charges = client.charges.list(customer=customer_id, limit=limit)
return [
{
"id": c.id,
"amount": c.amount,
"currency": c.currency,
"created": c.created,
"status": c.status,
"description": c.description
}
for c in charges.data
]
The key stays in keychain. Claude never sees it. This creates a stronger trust boundary than most developers assume.
Enable Subprocess Scrubbing Now
Without CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1, credentials in Claude Code's environment can leak to child processes. If STRIPE_API_KEY is in your environment and Claude spawns a bash tool, that variable is accessible.
Add this to your shell configuration:
export CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1
This closes a specific attack vector where compromised hooks or MCP servers could read credentials from the environment.
The Real Risk: Prompt Injection in Returned Data
Here's the attack surface people underestimate. Your plugin reads customer data from Stripe and returns it to Claude. What's in that data? Whatever the customer put there—including potentially malicious prompt injections in metadata fields.
If your plugin returns raw API objects directly, you're feeding potential prompt injections into Claude's context. Always sanitize or structure the data before returning it (like the example above that returns specific fields, not raw objects).
Defense in Depth: Three Independent Layers
Build security with three layers, so if one fails, the others still protect you:
- Plugin code hardcoded to read endpoints - No write functions exist in the plugin
- Restricted API keys at the provider level - Create keys scoped to specific resources and permissions
- Keychain storage - Prevents plaintext credential exposure
If the plugin code is bypassed, the restricted key prevents writes. If the key is misconfigured, the plugin code won't attempt writes. If both fail, at least the key wasn't trivially accessible.
Practical Takeaways for Plugin Developers
- Declare credential requirements in your plugin manifest so they get stored in keychain on install
- Enable
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1in your environment - Create restricted API keys at the provider level with minimum permissions
- Return structured data, not raw API objects to prevent prompt injection
- Keep plugin scope narrow - less surface area for manipulation
The keychain feature is one piece of the security story, not the whole solution. Proper architecture matters more than the storage mechanism.





