API Key Management: Secure Generation, Storage, and Rotation

API Key Management: Secure Generation, Storage, and Rotation

Whitespots Team ·
api
api-keys
authentication
secrets

Introduction

API keys are critical credentials that require careful management. Poor API key practices lead to unauthorized access, data breaches, and service abuse. This guide covers secure API key management from generation to rotation.

Secure API Key Generation

javascript
const crypto = require('crypto'); function generateApiKey() { // Generate cryptographically secure random key const key = crypto.randomBytes(32).toString('base64url'); const prefix = 'sk_live_'; // Identifiable prefix return `${prefix}${key}`; } // Hash key for storage function hashApiKey(apiKey) { return crypto .createHash('sha256') .update(apiKey) .digest('hex'); } // Create API key app.post('/api/keys', authenticate, async (req, res) => { const { name, scopes } = req.body; const apiKey = generateApiKey(); const keyHash = hashApiKey(apiKey); const record = await db.apiKeys.create({ userId: req.user.id, name, keyHash, scopes: scopes || [], createdAt: new Date(), lastUsedAt: null }); // Return key only once res.json({ id: record.id, key: apiKey, // Show only on creation! name, scopes }); });

API Key Authentication

javascript
// API key middleware async function authenticateApiKey(req, res, next) { const authHeader = req.headers['authorization']; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ error: 'Missing API key' }); } const apiKey = authHeader.substring(7); const keyHash = hashApiKey(apiKey); const record = await db.apiKeys.findOne({ keyHash, active: true, expiresAt: { $gt: new Date() } }); if (!record) { return res.status(401).json({ error: 'Invalid API key' }); } // Update last used timestamp await db.apiKeys.update(record.id, { lastUsedAt: new Date() }); req.apiKey = record; req.user = await db.users.findById(record.userId); next(); } // Apply to API routes app.use('/api/v1', authenticateApiKey);

Scoped Permissions

javascript
// Check API key scopes function requireScope(...requiredScopes) { return (req, res, next) => { const keyScopes = req.apiKey.scopes || []; const hasAllScopes = requiredScopes.every( scope => keyScopes.includes(scope) ); if (!hasAllScopes) { return res.status(403).json({ error: 'Insufficient permissions', required: requiredScopes, current: keyScopes }); } next(); }; } // Usage app.get('/api/v1/users', authenticateApiKey, requireScope('users:read'), getUsersHandler ); app.post('/api/v1/payments', authenticateApiKey, requireScope('payments:write'), createPaymentHandler );

Rate Limiting per API Key

javascript
const rateLimit = require('express-rate-limit'); const RedisStore = require('rate-limit-redis'); const apiKeyLimiter = rateLimit({ store: new RedisStore({ client: redis, prefix: 'rl:apikey:' }), windowMs: 60 * 1000, // 1 minute max: async (req) => { // Different limits based on plan const user = await db.users.findById(req.apiKey.userId); return user.plan === 'premium' ? 1000 : 100; }, keyGenerator: (req) => req.apiKey.id, handler: (req, res) => { res.status(429).json({ error: 'Rate limit exceeded', limit: req.rateLimit.limit, remaining: 0, resetTime: req.rateLimit.resetTime }); } }); app.use('/api/v1', authenticateApiKey, apiKeyLimiter);

Key Rotation

javascript
// Rotate API key app.post('/api/keys/:id/rotate', authenticate, async (req, res) => { const existingKey = await db.apiKeys.findOne({ id: req.params.id, userId: req.user.id }); if (!existingKey) { return res.status(404).json({ error: 'Key not found' }); } // Generate new key const newApiKey = generateApiKey(); const newKeyHash = hashApiKey(newApiKey); // Update record await db.apiKeys.update(existingKey.id, { keyHash: newKeyHash, rotatedAt: new Date() }); res.json({ id: existingKey.id, key: newApiKey, // Return new key rotatedAt: new Date() }); }); // Automatic expiration async function expireOldKeys() { const expiryDate = new Date(); expiryDate.setDate(expiryDate.getDate() - 90); // 90 days await db.apiKeys.updateMany( { createdAt: { $lt: expiryDate }, rotatedAt: null }, { active: false, expiredAt: new Date() } ); }

API Key Best Practices

  • ✅ Use cryptographically secure generation
  • ✅ Hash keys before storage
  • ✅ Never log full API keys
  • ✅ Implement key scopes/permissions
  • ✅ Set expiration dates
  • ✅ Enable key rotation
  • ✅ Rate limit per key
  • ✅ Monitor key usage
  • ✅ Revoke compromised keys immediately
  • ✅ Use prefixes for identification
  • ✅ Audit key activity
  • ✅ Allow multiple keys per user

Conclusion

Secure API key management requires proper generation, hashing, scoping, and rotation practices. Implement comprehensive key lifecycle management with monitoring and regular rotation to maintain API security.

Related