An API key with full administrative access is a skeleton key: if it leaks — through a log line, a git commit, a compromised developer laptop — the attacker inherits every permission you have. OWASP A01 (Broken Access Control) and NIST AC-6 (Least Privilege) both require limiting credentials to only the operations they need. A Stripe key scoped to charge creation cannot be used to delete customer data or issue refunds. A SendGrid key scoped to sending cannot expose your contact list. PCI-DSS 4.0 Req-7.2 requires that access to system components is appropriately defined and granted — overly broad API keys violate this at the integration layer.
High because an over-permissioned API key that leaks grants an attacker the same broad access as the application, expanding the blast radius of any credential compromise.
Audit each external service's dashboard and create restricted keys with only the permissions your application actually exercises. Document each key's scope in your secrets manager description field.
// src/config/api-keys.ts — one scoped key per service per environment
export const stripeKey = process.env.STRIPE_RESTRICTED_KEY // charges + webhooks only
export const openaiKey = process.env.OPENAI_API_KEY // completions only, no admin
export const sendgridKey = process.env.SENDGRID_MAIL_ONLY_KEY // mail send only
Stripe: Dashboard → Developers → API keys → Create restricted key → select "Charges: Write" and "Webhooks: Read" only. SendGrid: Settings → API Keys → Restricted Access → Mail Send. Generate new restricted keys, update your secrets manager, then revoke the old full-access keys.
ID: environment-security.secrets-management.api-keys-scoped
Severity: high
What to look for: For each external API (Stripe, OpenAI, SendGrid, etc.), check what permissions the configured API key has. Review API key settings in the service's dashboard or documentation. Check if separate keys are used for different services (e.g., Stripe, OpenAI, SendGrid all have different keys) rather than a single shared key.
Pass criteria: Enumerate all API keys used across the project. Each API key is scoped to only the minimum permissions needed by the application. Different services use different keys with at least 100% using environment-specific values. API keys are not shared across services or environments.
Fail criteria: A single API key grants broad permissions across multiple services, or keys are reused across development, staging, and production. Must not pass when the same API key value appears in multiple environment configs.
Skip (N/A) when: The project does not use external APIs with API key authentication (purely internal services or OAuth-only).
Detail on fail: Specify which keys have over-broad permissions. Example: "Stripe API key grants full administrative access; should be limited to creating charges and managing payments only" or "Same API key used across development, staging, and production".
Remediation: Audit API key permissions in each service's dashboard and restrict them:
Stripe: Create restricted API keys with limited scopes (e.g., "Create charges" only, not "Manage disputes" or "Manage accounts"). OpenAI: Create separate API keys per environment; scope to specific models if supported. SendGrid: Create keys with "Mail Send" permission only, not "Full Access".
Generate new restricted keys, update your secrets manager, and retire old keys:
// src/config/api-keys.ts — each service uses its own scoped key
export const stripeKey = process.env.STRIPE_RESTRICTED_KEY // charges only
export const openaiKey = process.env.OPENAI_API_KEY // env-specific