DEV Community

Cover image for 10 Rookie Mistakes That Leak Your API Keys (And How to Secure Them)
Akash Raidas
Akash Raidas

Posted on

10 Rookie Mistakes That Leak Your API Keys (And How to Secure Them)

You've just built your first app that uses the OpenAI API, a payment gateway, or a cloud service. Everything works perfectly on your machine. You're proud of your code, so you push it to GitHub to show off your work.

Three hours later, you get an email: "Unusual activity detected on your account." Your $200 API credit is gone. Someone in a different continent is running crypto mining operations using your key.

Sound like a nightmare? It happens more often than you think. In 2023 alone, thousands of API keys were leaked on GitHub, costing developers and companies millions of dollars.

The good news? Most of these leaks are completely preventable. Let's walk through the 10 most common mistakes beginners make with API keys and how to fix them before they cost you.

1. Hardcoding Keys Directly in Your Code

The Mistake:

# main.py
import openai

openai.api_key = "sk-proj-abc123xyz789..."  # DON'T DO THIS!

response = openai.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "Hello!"}]
)
Enter fullscreen mode Exit fullscreen mode

This is the number one rookie mistake. Your API key is sitting right there in your source code, visible to anyone who has access to your repository.

The Fix:

Use environment variables with a .env file:

# main.py
import os
from dotenv import load_dotenv
import openai

load_dotenv()  # Load environment variables from .env file

openai.api_key = os.getenv("OPENAI_API_KEY")

response = openai.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "Hello!"}]
)
Enter fullscreen mode Exit fullscreen mode

Create a .env file in your project root:

# .env
OPENAI_API_KEY=sk-proj-abc123xyz789...
DATABASE_URL=postgresql://user:pass@localhost/db
STRIPE_SECRET_KEY=sk_test_abc123...
Enter fullscreen mode Exit fullscreen mode

Install the python-dotenv package:

pip install python-dotenv
Enter fullscreen mode Exit fullscreen mode

For Node.js projects, use the dotenv package:

require('dotenv').config();
const apiKey = process.env.OPENAI_API_KEY;
Enter fullscreen mode Exit fullscreen mode

2. Forgetting to Add .env to .gitignore

The Mistake:

You created a .env file (great!), but you forgot to tell Git to ignore it. Now your secret keys are in your commit history forever.

Even if you delete the file later, it remains in your Git history. Anyone who clones your repo can see every commit you've ever made.

The Fix:

Create or update your .gitignore file before your first commit:

# .gitignore
.env
.env.local
.env.*.local
*.env

# Also ignore these common secret files
secrets.yml
config/secrets.yml
.credentials
Enter fullscreen mode Exit fullscreen mode

Already committed your .env file? You need to remove it from Git history:

# Remove from current commit
git rm --cached .env

# Remove from entire history (use with caution!)
git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch .env" \
  --prune-empty --tag-name-filter cat -- --all
Enter fullscreen mode Exit fullscreen mode

Then immediately rotate (change) any exposed API keys.

3. Using the Same API Key for Development and Production

The Mistake:

You're using your production API key while testing locally. During development, you make 1,000 test requests, hit rate limits, or accidentally delete production data.

The Fix:

Always maintain separate keys for different environments:

# .env.development
STRIPE_KEY=sk_test_abc123...
DATABASE_URL=postgresql://localhost/myapp_dev

# .env.production
STRIPE_KEY=sk_live_xyz789...
DATABASE_URL=postgresql://prod-server/myapp_prod
Enter fullscreen mode Exit fullscreen mode

Most API providers offer test/sandbox keys specifically for development:

  • Stripe: sk_test_... vs sk_live_...
  • OpenAI: Separate API keys with different rate limits
  • AWS: Different IAM users for dev/staging/production
  • Twilio: Test credentials that don't send real SMS

This practice also helps you:

  • Avoid accidentally charging real credit cards during testing
  • Keep your production rate limits intact
  • Separate development costs from production costs
  • Test safely without fear of breaking production

4. Granting "All Access" Permissions Instead of Least Privilege

The Mistake:

When creating an API key, you select "Full Access" or "Admin" permissions because it's easier than figuring out what you actually need.

If that key leaks, an attacker has complete control over your account: they can delete data, modify settings, or rack up huge bills.

The Fix:

Follow the principle of least privilege: only grant the minimum permissions required for the task.

Example with AWS IAM:

Instead of:

{
  "Effect": "Allow",
  "Action": "*",
  "Resource": "*"
}
Enter fullscreen mode Exit fullscreen mode

Use:

{
  "Effect": "Allow",
  "Action": [
    "s3:GetObject",
    "s3:PutObject"
  ],
  "Resource": "arn:aws:s3:::my-specific-bucket/*"
}
Enter fullscreen mode Exit fullscreen mode

Example with GitHub Personal Access Tokens:

Don't select all scopes. If you're just reading repository data, only enable:

  • repo:status
  • public_repo

Example with Database Users:

-- Bad: Full admin access
GRANT ALL PRIVILEGES ON *.* TO 'app_user'@'localhost';

-- Good: Only what's needed
GRANT SELECT, INSERT, UPDATE ON myapp_db.* TO 'app_user'@'localhost';
Enter fullscreen mode Exit fullscreen mode

Many services also offer read-only keys. Use them whenever you're only fetching data.

5. Storing Keys in Plain Text in Notion, Trello, or Docs

The Mistake:

You keep a Notion page or Google Doc titled "API Keys and Passwords" where you paste all your credentials for "easy access." Anyone with access to that doc (current or former team members, people you've shared links with) can see everything.

These documents also:

  • Sync to cloud services
  • Appear in search results
  • Get cached in your browser
  • Can be accidentally shared via public links

The Fix:

Use a proper password manager or secret management tool:

For Personal Projects:

  • 1Password: Has a developer-friendly CLI tool
  • Bitwarden: Open-source password manager
  • LastPass: Popular option with team features

For Team Projects:

  • HashiCorp Vault: Industry standard for secret management
  • AWS Secrets Manager: If you're on AWS
  • Azure Key Vault: For Azure users
  • Google Secret Manager: For GCP users

Example with 1Password CLI:

# Store a secret
op item create --category=login \
  --title="OpenAI API Key" \
  password="sk-proj-abc123..."

# Retrieve in your script
export OPENAI_API_KEY=$(op read "op://Private/OpenAI API Key/password")
Enter fullscreen mode Exit fullscreen mode

If you must document keys temporarily (during onboarding, for example), use encrypted storage or time-limited secret sharing services like OneTimeSecret.

6. Exposing Keys in Client-Side JavaScript

The Mistake:

You're building a web app and include your API key directly in your JavaScript because the frontend needs to make API calls:

// DON'T DO THIS!
const apiKey = "sk-proj-abc123...";

fetch('https://api.openai.com/v1/chat/completions', {
  headers: {
    'Authorization': `Bearer ${apiKey}`
  }
});
Enter fullscreen mode Exit fullscreen mode

Anyone can open DevTools, view your source code, and copy your API key in seconds.

The Fix:

Never put secret keys in client-side code. Instead:

Option 1: Use a Backend Proxy

Create an API route on your server that makes the actual API call:

// Frontend (safe)
fetch('/api/chat', {
  method: 'POST',
  body: JSON.stringify({ message: 'Hello' })
});

// Backend (Node.js/Express)
app.post('/api/chat', async (req, res) => {
  const apiKey = process.env.OPENAI_API_KEY;  // Secure!

  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    headers: { 'Authorization': `Bearer ${apiKey}` },
    body: JSON.stringify({...})
  });

  res.json(await response.json());
});
Enter fullscreen mode Exit fullscreen mode

Option 2: Use Restricted Public Keys

Some services offer client-side keys with restrictions:

  • Firebase: Restrict by domain
  • Google Maps: Restrict by HTTP referrer or IP
  • Stripe: Use publishable keys (pk_) for client-side

Option 3: Use Serverless Functions

Deploy API calls as serverless functions (Vercel, Netlify, AWS Lambda) that keep secrets server-side.

7. Never Rotating Your API Keys

The Mistake:

You created an API key two years ago and have been using it ever since. You've shared it with contractors, used it on multiple machines, and included it in old projects you've forgotten about.

The Fix:

Rotate your API keys regularly, especially:

  • Every 90 days as a standard practice
  • Immediately when an employee leaves
  • After any suspected compromise
  • When decommissioning old projects
  • After sharing keys in demos or screenshots

How to Rotate Safely:

  1. Generate a new key
  2. Update your production environment with the new key
  3. Test thoroughly
  4. Delete the old key
  5. Update documentation

Most platforms let you have multiple active keys simultaneously, making rotation seamless:

# Have both old and new keys active
OPENAI_KEY_OLD=sk-proj-abc123...
OPENAI_KEY_NEW=sk-proj-xyz789...

# Deploy new key to production
# Verify it works
# Delete old key
Enter fullscreen mode Exit fullscreen mode

Set reminders or use tools like AWS IAM Access Analyzer to identify unused keys.

8. Logging API Keys in Error Messages or Debug Logs

The Mistake:

Your application logs everything for debugging purposes, including the full request headers or environment variables:

import logging

logging.debug(f"Making API call with headers: {headers}")
# Logs: Making API call with headers: {'Authorization': 'Bearer sk-proj-abc123...'}

logging.error(f"Environment: {os.environ}")
# Logs ALL environment variables including secrets!
Enter fullscreen mode Exit fullscreen mode

These logs often end up in:

  • Log aggregation services (Datadog, Splunk, CloudWatch)
  • Error tracking tools (Sentry, Rollbar)
  • Shared with support teams
  • Committed to files in your repository

The Fix:

Sanitize sensitive data before logging:

import logging
import re

def sanitize_headers(headers):
    safe_headers = headers.copy()
    if 'Authorization' in safe_headers:
        safe_headers['Authorization'] = 'Bearer [REDACTED]'
    return safe_headers

logging.debug(f"Making API call with headers: {sanitize_headers(headers)}")

# For environment variables
def log_safe_env():
    safe_env = {k: v for k, v in os.environ.items() 
                if not any(secret in k.upper() 
                          for secret in ['KEY', 'SECRET', 'PASSWORD', 'TOKEN'])}
    return safe_env
Enter fullscreen mode Exit fullscreen mode

Configure your logging framework to automatically redact secrets:

# Python logging config
class SensitiveDataFilter(logging.Filter):
    def filter(self, record):
        record.msg = re.sub(r'sk-[a-zA-Z0-9]{48}', '[REDACTED]', str(record.msg))
        return True

logger.addFilter(SensitiveDataFilter())
Enter fullscreen mode Exit fullscreen mode

For error tracking services like Sentry:

import sentry_sdk

sentry_sdk.init(
    dsn="your-dsn",
    before_send=lambda event, hint: scrub_sensitive_data(event)
)
Enter fullscreen mode Exit fullscreen mode

9. Accidentally Including Keys in Screenshots or Recordings

The Mistake:

You're creating a tutorial, recording a demo for your team, or taking a screenshot to report a bug. Your API key is visible in your code editor, terminal, or browser DevTools.

Once that image or video is online, it's nearly impossible to fully remove. People download it, share it, and archive it.

The Fix:

Before recording or screenshotting:

  1. Use placeholder values:
# Instead of real key
OPENAI_API_KEY = "sk-proj-abc123..."

# Use placeholder
OPENAI_API_KEY = "your-api-key-here"
Enter fullscreen mode Exit fullscreen mode
  1. Use code comments:
# api_key = os.getenv("OPENAI_API_KEY")  # Hidden for demo
api_key = "demo-key-not-real"
Enter fullscreen mode Exit fullscreen mode
  1. Zoom in to hide sensitive areas
  2. Use screen recording software with blur features
  3. Edit screenshots to blur or redact keys before sharing

Tools to help:

  • macOS: Built-in Screenshot markup tools
  • Windows: Snipping Tool with pen/highlighter
  • Linux: Flameshot (has blur/pixelate features)
  • OBS Studio: Add blur filters for streaming

Pro tip: Create a separate "demo" environment with fake/limited keys specifically for recordings and presentations.

10. Not Using Environment-Specific Secret Management

The Mistake:

You're deploying to production and manually setting environment variables through your hosting provider's web dashboard. Team members don't know which keys are active, there's no audit trail, and updating keys requires manual work across multiple services.

The Fix:

Use proper secret management tools that integrate with your deployment pipeline:

For Docker:

# docker-compose.yml
services:
  app:
    env_file:
      - .env.production
    secrets:
      - db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt
Enter fullscreen mode Exit fullscreen mode

For Kubernetes:

apiVersion: v1
kind: Secret
metadata:
  name: api-keys
type: Opaque
data:
  openai-key: <base64-encoded-key>
---
apiVersion: v1
kind: Pod
metadata:
  name: myapp
spec:
  containers:
  - name: myapp
    env:
    - name: OPENAI_API_KEY
      valueFrom:
        secretKeyRef:
          name: api-keys
          key: openai-key
Enter fullscreen mode Exit fullscreen mode

For CI/CD Pipelines:

Use encrypted secrets in:

  • GitHub Actions: Repository secrets
  • GitLab CI: Masked variables
  • CircleCI: Project environment variables (restricted)
  • Jenkins: Credentials plugin
# GitHub Actions
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Deploy
        env:
          OPENAI_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: ./deploy.sh
Enter fullscreen mode Exit fullscreen mode

For Modern Platforms:

  • Vercel: Environment variables per deployment environment
  • Netlify: Build environment variables
  • Railway: Service variables with automatic injection
  • Render: Secret files and environment groups

These platforms provide:

  • Automatic injection into your runtime
  • Team access control
  • Audit logs of who accessed what
  • Easy rotation without redeployment

Bonus: What to Do If You've Already Leaked a Key

Don't panic, but act quickly:

  1. Immediately rotate the key

    • Generate a new key
    • Delete the compromised key
    • Update all services using it
  2. Check for unauthorized usage

    • Review your API usage dashboard
    • Look for unexpected spikes
    • Check for unfamiliar IP addresses
  3. Assess the damage

    • Review billing for unexpected charges
    • Check if data was accessed or modified
    • Look for new resources created
  4. Remove from Git history (if applicable)

   git filter-branch --force --index-filter \
     "git rm --cached --ignore-unmatch .env" \
     --prune-empty --tag-name-filter cat -- --all

   git push origin --force --all
Enter fullscreen mode Exit fullscreen mode
  1. Notify your team

    • Inform relevant stakeholders
    • Update documentation
    • Review your security practices
  2. Enable additional security

    • Turn on two-factor authentication
    • Enable IP allowlisting if available
    • Set up billing alerts
    • Configure rate limits

Key Takeaways

Securing API keys doesn't have to be complicated. Follow these core principles:

  1. Never commit secrets to version control - Use .env files and .gitignore
  2. Use environment variables - Never hardcode credentials
  3. Follow least privilege - Grant only necessary permissions
  4. Separate environments - Different keys for dev, staging, and production
  5. Rotate regularly - Change keys periodically and after incidents
  6. Use proper tools - Password managers and secret managers exist for a reason
  7. Stay vigilant - Review logs, audit access, monitor usage

The few minutes you spend setting up proper secret management will save you from hours of panic and potentially thousands of dollars in damages.

Your future self (and your wallet) will thank you.


Have you ever accidentally exposed an API key? Share your story in the comments - we've all been there! And if you found this helpful, consider sharing it with someone who's just starting their development journey.

Want to learn more about security? Check out OWASP's API Security Top 10 for even more ways to protect your applications.

Top comments (0)