API Key Security: Secrets That Shouldn't Be Secrets
TL;DR
API keys are the skeleton keys to your infrastructure. Learn how they get leaked, why attackers love them, and how to implement proper secrets management that actually works.
Every week, security researchers find thousands of API keys exposed in public repositories. AWS keys, Stripe tokens, database credentials, OAuth secrets—all sitting in plain text, waiting to be exploited. In 2024 alone, exposed credentials led to breaches costing organizations billions of dollars.
This isn't a new problem, yet it keeps happening. Why? Because managing secrets is harder than it looks, and developers are under pressure to ship fast. In this comprehensive guide, we'll explore how API keys get leaked, what attackers do with them, and how to build a secrets management strategy that actually works.
The Anatomy of an API Key Leak
API key leaks rarely happen because of malicious insiders. They happen because of human error in everyday development workflows. Understanding these patterns is the first step to preventing them.
The Git Commit Mistake
The most common leak: a developer hardcodes a key for testing, commits it to Git, and pushes to a public repository. Even if they immediately realize the mistake and remove the key in the next commit, it's too late. Git history is permanent, and automated scanners are watching.
// config.js - The mistake that costs millions
const config = {
stripe: {
secretKey: 'sk_live_51ABC123xyz789...', // NEVER DO THIS
publishableKey: 'pk_live_51ABC123xyz789...'
},
aws: {
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
}
};Automated bots scan GitHub in real-time. Within seconds of pushing a commit containing an AWS key, attackers can detect it, extract it, and begin exploiting your account. We've seen cryptocurrency miners deployed within minutes of key exposure.
The Environment Variable Exposure
Developers often think environment variables are safe because they're not in the code. But environment variables can leak through:
Docker images: Environment variables set during build are visible in image layers.
Error pages: Debug modes often dump environment variables in stack traces.
Client-side bundles: Build tools may inline NEXT_PUBLIC_* or REACT_APP_* variables into JavaScript.
CI/CD logs: Build logs often print environment variables, and these logs may be publicly accessible.
The Configuration File Oversight
Many frameworks use configuration files that developers accidentally commit:
# Files that commonly contain secrets
.env
.env.local
.env.production
config.json
secrets.yaml
credentials.json
serviceAccountKey.json
*.pem
*.keyEven with .gitignore, developers sometimes force-add files or copy configurations into new projects without cleaning them.
What Attackers Do With Leaked Keys
The impact of a leaked API key depends on what it accesses. Here's what attackers typically do:
AWS Keys: Spin up cryptocurrency miners (running up massive bills), access S3 buckets containing customer data, pivot to other AWS services, or deploy ransomware.
Stripe Keys: Create fraudulent refunds, access customer payment information, or transfer funds to attacker-controlled accounts.
Database Credentials: Exfiltrate all data, inject ransomware, or sell access on dark web forums.
OAuth Client Secrets: Impersonate the application to access user data from third-party APIs.
SendGrid/Mailgun Keys: Send phishing emails that appear to come from your domain, destroying your sender reputation.
Prevention: A Multi-Layer Approach
Layer 1: Pre-Commit Hooks
Stop secrets before they enter Git history. Tools like git-secrets, pre-commit, and Husky can scan commits for patterns that look like API keys.
# Install git-secrets
brew install git-secrets
# Register AWS patterns
git secrets --register-aws
# Add custom patterns for your secrets
git secrets --add 'sk_live_[a-zA-Z0-9]{24}'
git secrets --add 'PRIVATE KEY'
# Install the hook
git secrets --installLayer 2: CI/CD Scanning
Even with pre-commit hooks, secrets can slip through. Add scanning to your CI pipeline as a second line of defense:
# GitHub Actions example
name: Secret Scanning
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: TruffleHog Scan
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
extra_args: --only-verifiedLayer 3: Secrets Management Systems
Don't store secrets in environment variables or files. Use a dedicated secrets manager:
HashiCorp Vault: Industry standard, supports dynamic secrets, automatic rotation, and detailed audit logs.
AWS Secrets Manager: Native AWS integration, automatic rotation for RDS credentials.
Doppler: Developer-friendly, syncs with all major platforms and CI/CD tools.
1Password/Bitwarden Teams: Good for smaller teams, integrates with CLI workflows.
// Instead of hardcoded secrets
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
async function getSecret(secretName) {
const client = new SecretsManagerClient({ region: 'us-east-1' });
const response = await client.send(
new GetSecretValueCommand({ SecretId: secretName })
);
return JSON.parse(response.SecretString);
}
// Retrieve secrets at runtime
const dbCredentials = await getSecret('prod/database');Layer 4: Short-Lived Credentials
The best secret is one that expires quickly. Instead of long-lived API keys, use:
IAM Roles: For AWS services, use IAM roles instead of access keys. Credentials are automatically rotated.
OAuth 2.0 with short expiry: Access tokens that expire in minutes, with refresh tokens stored securely.
Dynamic database credentials: Vault can create database users on-demand that automatically expire.
Responding to a Leak
When you discover a leaked key, time is critical. Here's your action plan:
1. Revoke immediately (within minutes): Don't wait to assess impact. Revoke the key first, ask questions later. Every minute the key is active is another minute attackers can use it.
2. Generate a new key: Create a new key through proper channels and update your secrets manager.
3. Audit for unauthorized access: Check logs for any usage of the leaked key. Look for unusual IP addresses, times, or operations.
4. Clean Git history: Use git-filter-repo or BFG Repo Cleaner to remove the secret from all commits. Note: This requires force-pushing and coordinating with all contributors.
5. Conduct a post-mortem: How did the secret get committed? What controls failed? What changes will prevent recurrence?
The Principle of Least Privilege
Every API key should have the minimum permissions necessary for its purpose. A key used for reading analytics shouldn't be able to delete resources. A development key shouldn't work in production.
When creating API keys, ask: "What's the worst that could happen if this key is leaked?" Then scope the key's permissions to minimize that impact.
Conclusion
API key security isn't glamorous, but it's foundational. A single leaked key can undo years of security investment. The good news is that proper secrets management is a solved problem—you just need to implement it.
Start today: audit your repositories for hardcoded secrets, set up pre-commit hooks, and evaluate a secrets management solution. Your future self (and your security team) will thank you.
Practice finding leaked secrets in realistic scenarios with AliceSec's Secrets Exposure challenges.
Get the weekly vulnerability breakdown
New challenges, exploit techniques, and security tips. No spam.
Unsubscribe anytime. No spam, ever.