Skip to main content

api-security-checklist

Comprehensive API security checklist and best practices for designing, testing, and securing REST, GraphQL, and OAuth APIs

Quellinformationen

Repository
reason-machines/security-skills
Letzte Quellaktivität
30. Juli 2026 um 22:52
Erkannte Sprache von SKILL.md
Englisch
Sterne
11
Forks
1

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
api-security-checklist
description
Comprehensive API security checklist and best practices for designing, testing, and securing REST, GraphQL, and OAuth APIs
triggers
["show me API security best practices","how do I secure my REST API","what security checks should I implement for my API","help me review API security","OAuth security recommendations","API authentication and authorization checklist","protect my API from attacks","API security audit checklist"]
# API Security Checklist Skill > Skill by [ara.so](https://ara.so) — Security Skills collection. This skill provides comprehensive guidance on API security best practices based on the widely-adopted API Security Checklist. Use this to design, audit, and secure REST, GraphQL, and OAuth APIs against common vulnerabilities and attack vectors. ## Overview The API Security Checklist covers critical security countermeasures across: - **Authentication** - Secure user identity verification - **Authorization** - Access control and OAuth flows - **Input Validation** - Preventing injection attacks - **Output Security** - Secure response handling - **Processing** - Backend security measures - **Monitoring** - Detection and alerting - **CI/CD** - Secure development lifecycle ## Installation This is a knowledge resource, not a software package. To use: 1. **Bookmark for reference**: Keep the checklist accessible during API development 2. **Integrate into code reviews**: Use as a PR checklist template 3. **Add to CI/CD**: Implement automated checks based on these guidelines 4. **Security audits**: Use as an audit framework ## Authentication Security ### ❌ Avoid Basic Auth ```javascript // BAD - Basic Auth is insecure app.get('/api/users', (req, res) => { const auth = req.headers.authorization; const [user, pass] = Buffer.from(auth.split(' ')[1], 'base64').toString().split(':'); // Don't do this! }); ``` ### ✅ Use Standard Authentication ```javascript // GOOD - JWT with proper validation const jwt = require('jsonwebtoken'); function authenticateToken(req, res, next) { const token = req.headers['authorization']?.split(' ')[1]; if (!token) return res.sendStatus(401); jwt.verify(token, process.env.JWT_SECRET, (err, user) => { if (err) return res.sendStatus(403); req.user = user; next(); }); } app.get('/api/users', authenticateToken, (req, res) => { res.json({ user: req.user }); }); ``` ### Rate Limiting and Max Retry ```javascript const rateLimit = require('express-rate-limit'); // Limit login attempts const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 5, // 5 attempts message: 'Too many login attempts, please try again later', standardHeaders: true, legacyHeaders: false, }); app.post('/api/login', loginLimiter, async (req, res) => { // Login logic }); ``` ### Password Storage ```javascript const bcrypt = require('bcrypt'); // GOOD - Hash passwords with bcrypt async function hashPassword(password) { const saltRounds = 12; return await bcrypt.hash(password, saltRounds); } async function verifyPassword(password, hash) { return await bcrypt.compare(password, hash); } // Usage app.post('/api/register', async (req, res) => { const { email, password } = req.body; const hashedPassword = await hashPassword(password); // Store hashedPassword in database }); ``` ## Access Control ### HTTPS and Security Headers ```javascript const helmet = require('helmet'); const express = require('express'); const app = express(); // Use Helmet for security headers app.use(helmet({ hsts: { maxAge: 31536000, includeSubDomains: true, preload: true }, contentSecurityPolicy: { directives: { defaultSrc: ["'none'"] } }, frameguard: { action: 'deny' }, noSniff: true })); // Force HTTPS in production if (process.env.NODE_ENV === 'production') { app.use((req, res, next) => { if (!req.secure) { return res.redirect('https://' + req.headers.host + req.url); } next(); }); } ``` ### IP Whitelisting for Private APIs ```javascript const ipWhitelist = process.env.ALLOWED_IPS?.split(',') || []; function checkIPWhitelist(req, res, next) { const clientIP = req.ip || req.connection.remoteAddress; if (!ipWhitelist.includes(clientIP)) { return res.status(403).json({ error: 'IP not authorized' }); } next(); } app.use('/api/admin', checkIPWhitelist); ``` ### DDoS Protection ```javascript const rateLimit = require('express-rate-limit'); // General API rate limiting const apiLimiter = rateLimit({ windowMs: 1 * 60 * 1000, // 1 minute max: 100, // 100 requests per minute message: 'Too many requests from this IP' }); app.use('/api/', apiLimiter); ``` ## OAuth Security ### Validate Redirect URI ```python from urllib.parse import urlparse ALLOWED_REDIRECT_URIS = [ 'https://app.example.com/callback', 'https://app.example.com/oauth/callback' ] def validate_redirect_uri(redirect_uri): """Always validate redirect_uri server-side""" if redirect_uri not in ALLOWED_REDIRECT_URIS: raise ValueError('Invalid redirect_uri') return True # In OAuth authorization endpoint @app.route('/oauth/authorize') def authorize(): redirect_uri = request.args.get('redirect_uri') try: validate_redirect_uri(redirect_uri) except ValueError: return {'error': 'invalid_redirect_uri'}, 400 # Continue with authorization ``` ### Use State Parameter for CSRF Protection ```javascript const crypto = require('crypto'); // Generate state parameter function generateState() { return crypto.randomBytes(32).toString('hex'); } // OAuth authorization request app.get('/oauth/login', (req, res) => { const state = generateState(); // Store state in session req.session.oauthState = state; const authUrl = `https://provider.com/oauth/authorize?` + `client_id=${process.env.OAUTH_CLIENT_ID}` + `&redirect_uri=${encodeURIComponent(process.env.OAUTH_REDIRECT_URI)}` + `&response_type=code` + `&state=${state}` + `&scope=read`; res.redirect(authUrl); }); // OAuth callback - validate state app.get('/oauth/callback', (req, res) => { const { code, state } = req.query; // Validate state parameter if (state !== req.session.oauthState) { return res.status(403).json({ error: 'Invalid state parameter' }); } // Exchange code for token // Never use response_type=token (implicit flow) }); ``` ### Scope Validation ```javascript const VALID_SCOPES = ['read', 'write', 'admin']; const DEFAULT_SCOPE = 'read'; function validateScopes(requestedScopes) { if (!requestedScopes) return [DEFAULT_SCOPE]; const scopes = requestedScopes.split(' '); const validScopes = scopes.filter(scope => VALID_SCOPES.includes(scope)); return validScopes.length > 0 ? validScopes : [DEFAULT_SCOPE]; } app.post('/oauth/token', (req, res) => { const requestedScopes = req.body.scope; const allowedScopes = validateScopes(requestedScopes); // Generate token with validated scopes only const token = jwt.sign( { scopes: allowedScopes }, process.env.JWT_SECRET, { expiresIn: '1h' } ); res.json({ access_token: token, scope: allowedScopes.join(' ') }); }); ``` ## Input Validation ### HTTP Method Validation ```javascript const ALLOWED_METHODS = { '/api/users': ['GET', 'POST'], '/api/users/:id': ['GET', 'PUT', 'PATCH', 'DELETE'] }; function validateMethod(req, res, next) { const allowedForRoute = ALLOWED_METHODS[req.route.path]; if (!allowedForRoute || !allowedForRoute.includes(req.method)) { res.set('Allow', allowedForRoute.join(', ')); return res.status(405).json({ error: 'Method Not Allowed' }); } next(); } app.use(validateMethod); ``` ### Content-Type Validation ```javascript const SUPPORTED_CONTENT_TYPES = [ 'application/json', 'application/xml' ]; function validateContentType(req, res, next) { // Validate Accept header const accept = req.headers.accept; const acceptsSupported = SUPPORTED_CONTENT_TYPES.some(type => accept?.includes(type) ); if (!acceptsSupported && accept !== '*/*') { return res.status(406).json({ error: 'Not Acceptable' }); } // Validate Content-Type for POST/PUT/PATCH if (['POST', 'PUT', 'PATCH'].includes(req.method)) { const contentType = req.headers['content-type']?.split(';')[0]; if (!SUPPORTED_CONTENT_TYPES.includes(contentType)) { return res.status(415).json({ error: 'Unsupported Media Type' }); } } next(); } app.use(validateContentType); ``` ### Input Sanitization ```javascript const validator = require('validator'); // Prevent XSS, SQL Injection, etc. function sanitizeInput(data) { if (typeof data === 'string') { return validator.escape(data); } if (Array.isArray(data)) { return data.map(sanitizeInput); } if (typeof data === 'object' && data !== null) { const sanitized = {}; for (const [key, value] of Object.entries(data)) { sanitized[key] = sanitizeInput(value); } return sanitized; } return data; } app.post('/api/users', (req, res) => { const sanitizedBody = sanitizeInput(req.body); // Use sanitizedBody instead of req.body }); ``` ### Prevent XXE (XML External Entity) ```javascript const libxmljs = require('libxmljs'); function parseXMLSafely(xmlString) { try { // Disable external entity parsing const doc = libxmljs.parseXml(xmlString, { noent: false, // Don't substitute entities nonet: true, // Don't access network dtdload: false // Don't load external DTDs }); return doc; } catch (error) { throw new Error('Invalid XML'); } } app.post('/api/data', (req, res) => { if (req.headers['content-type'] === 'application/xml') { try { const doc = parseXMLSafely(req.body); // Process document } catch (error) { return res.status(400).json({ error: 'Invalid XML' }); } } }); ``` ## Processing Security ### Avoid Auto-Increment IDs (Use UUIDs) ```javascript const { v4: uuidv4 } = require('uuid'); // GOOD - Use UUIDs instead of auto-increment IDs app.post('/api/users', async (req, res) => { const user = { id: uuidv4(), // e.g., '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d' ...req.body }; await db.users.create(user); res.json(user); }); ``` ### Use /me for User Resources ```javascript // BAD - Exposes user IDs
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen