davila7--claude-code-templates
1 行
24 KiB
JSON
1 行
24 KiB
JSON
{"content": "---\nname: api-security-best-practices\ndescription: \"Implement secure API design patterns including authentication, authorization, input validation, rate limiting, and protection against common API vulnerabilities\"\n---\n\n# API Security Best Practices\n\n## Overview\n\nGuide developers in building secure APIs by implementing authentication, authorization, input validation, rate limiting, and protection against common vulnerabilities. This skill covers security patterns for REST, GraphQL, and WebSocket APIs.\n\n## When to Use This Skill\n\n- Use when designing new API endpoints\n- Use when securing existing APIs\n- Use when implementing authentication and authorization\n- Use when protecting against API attacks (injection, DDoS, etc.)\n- Use when conducting API security reviews\n- Use when preparing for security audits\n- Use when implementing rate limiting and throttling\n- Use when handling sensitive data in APIs\n\n## How It Works\n\n### Step 1: Authentication & Authorization\n\nI'll help you implement secure authentication:\n- Choose authentication method (JWT, OAuth 2.0, API keys)\n- Implement token-based authentication\n- Set up role-based access control (RBAC)\n- Secure session management\n- Implement multi-factor authentication (MFA)\n\n### Step 2: Input Validation & Sanitization\n\nProtect against injection attacks:\n- Validate all input data\n- Sanitize user inputs\n- Use parameterized queries\n- Implement request schema validation\n- Prevent SQL injection, XSS, and command injection\n\n### Step 3: Rate Limiting & Throttling\n\nPrevent abuse and DDoS attacks:\n- Implement rate limiting per user/IP\n- Set up API throttling\n- Configure request quotas\n- Handle rate limit errors gracefully\n- Monitor for suspicious activity\n\n### Step 4: Data Protection\n\nSecure sensitive data:\n- Encrypt data in transit (HTTPS/TLS)\n- Encrypt sensitive data at rest\n- Implement proper error handling (no data leaks)\n- Sanitize error messages\n- Use secure headers\n\n### Step 5: API Security Testing\n\nVerify security implementation:\n- Test authentication and authorization\n- Perform penetration testing\n- Check for common vulnerabilities (OWASP API Top 10)\n- Validate input handling\n- Test rate limiting\n\n\n## Examples\n\n### Example 1: Implementing JWT Authentication\n\n```markdown\n## Secure JWT Authentication Implementation\n\n### Authentication Flow\n\n1. User logs in with credentials\n2. Server validates credentials\n3. Server generates JWT token\n4. Client stores token securely\n5. Client sends token with each request\n6. Server validates token\n\n### Implementation\n\n#### 1. Generate Secure JWT Tokens\n\n\\`\\`\\`javascript\n// auth.js\nconst jwt = require('jsonwebtoken');\nconst bcrypt = require('bcrypt');\n\n// Login endpoint\napp.post('/api/auth/login', async (req, res) => {\n try {\n const { email, password } = req.body;\n \n // Validate input\n if (!email || !password) {\n return res.status(400).json({ \n error: 'Email and password are required' \n });\n }\n \n // Find user\n const user = await db.user.findUnique({ \n where: { email } \n });\n \n if (!user) {\n // Don't reveal if user exists\n return res.status(401).json({ \n error: 'Invalid credentials' \n });\n }\n \n // Verify password\n const validPassword = await bcrypt.compare(\n password, \n user.passwordHash\n );\n \n if (!validPassword) {\n return res.status(401).json({ \n error: 'Invalid credentials' \n });\n }\n \n // Generate JWT token\n const token = jwt.sign(\n { \n userId: user.id,\n email: user.email,\n role: user.role\n },\n process.env.JWT_SECRET,\n { \n expiresIn: '1h',\n issuer: 'your-app',\n audience: 'your-app-users'\n }\n );\n \n // Generate refresh token\n const refreshToken = jwt.sign(\n { userId: user.id },\n process.env.JWT_REFRESH_SECRET,\n { expiresIn: '7d' }\n );\n \n // Store refresh token in database\n await db.refreshToken.create({\n data: {\n token: refreshToken,\n userId: user.id,\n expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)\n }\n });\n \n res.json({\n token,\n refreshToken,\n expiresIn: 3600\n });\n \n } catch (error) {\n console.error('Login error:', error);\n res.status(500).json({ \n error: 'An error occurred during login' \n });\n }\n});\n\\`\\`\\`\n\n#### 2. Verify JWT Tokens (Middleware)\n\n\\`\\`\\`javascript\n// middleware/auth.js\nconst jwt = require('jsonwebtoken');\n\nfunction authenticateToken(req, res, next) {\n // Get token from header\n const authHeader = req.headers['authorization'];\n const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN\n \n if (!token) {\n return res.status(401).json({ \n error: 'Access token required' \n });\n }\n \n // Verify token\n jwt.verify(\n token, \n process.env.JWT_SECRET,\n { \n issuer: 'your-app',\n audience: 'your-app-users'\n },\n (err, user) => {\n if (err) {\n if (err.name === 'TokenExpiredError') {\n return res.status(401).json({ \n error: 'Token expired' \n });\n }\n return res.status(403).json({ \n error: 'Invalid token' \n });\n }\n \n // Attach user to request\n req.user = user;\n next();\n }\n );\n}\n\nmodule.exports = { authenticateToken };\n\\`\\`\\`\n\n#### 3. Protect Routes\n\n\\`\\`\\`javascript\nconst { authenticateToken } = require('./middleware/auth');\n\n// Protected route\napp.get('/api/user/profile', authenticateToken, async (req, res) => {\n try {\n const user = await db.user.findUnique({\n where: { id: req.user.userId },\n select: {\n id: true,\n email: true,\n name: true,\n // Don't return passwordHash\n }\n });\n \n res.json(user);\n } catch (error) {\n res.status(500).json({ error: 'Server error' });\n }\n});\n\\`\\`\\`\n\n#### 4. Implement Token Refresh\n\n\\`\\`\\`javascript\napp.post('/api/auth/refresh', async (req, res) => {\n const { refreshToken } = req.body;\n \n if (!refreshToken) {\n return res.status(401).json({ \n error: 'Refresh token required' \n });\n }\n \n try {\n // Verify refresh token\n const decoded = jwt.verify(\n refreshToken, \n process.env.JWT_REFRESH_SECRET\n );\n \n // Check if refresh token exists in database\n const storedToken = await db.refreshToken.findFirst({\n where: {\n token: refreshToken,\n userId: decoded.userId,\n expiresAt: { gt: new Date() }\n }\n });\n \n if (!storedToken) {\n return res.status(403).json({ \n error: 'Invalid refresh token' \n });\n }\n \n // Generate new access token\n const user = await db.user.findUnique({\n where: { id: decoded.userId }\n });\n \n const newToken = jwt.sign(\n { \n userId: user.id,\n email: user.email,\n role: user.role\n },\n process.env.JWT_SECRET,\n { expiresIn: '1h' }\n );\n \n res.json({\n token: newToken,\n expiresIn: 3600\n });\n \n } catch (error) {\n res.status(403).json({ \n error: 'Invalid refresh token' \n });\n }\n});\n\\`\\`\\`\n\n### Security Best Practices\n\n- ✅ Use strong JWT secrets (256-bit minimum)\n- ✅ Set short expiration times (1 hour for access tokens)\n- ✅ Implement refresh tokens for long-lived sessions\n- ✅ Store refresh tokens in database (can be revoked)\n- ✅ Use HTTPS only\n- ✅ Don't store sensitive data in JWT payload\n- ✅ Validate token issuer and audience\n- ✅ Implement token blacklisting for logout\n```\n\n\n### Example 2: Input Validation and SQL Injection Prevention\n\n```markdown\n## Preventing SQL Injection and Input Validation\n\n### The Problem\n\n**❌ Vulnerable Code:**\n\\`\\`\\`javascript\n// NEVER DO THIS - SQL Injection vulnerability\napp.get('/api/users/:id', async (req, res) => {\n const userId = req.params.id;\n \n // Dangerous: User input directly in query\n const query = \\`SELECT * FROM users WHERE id = '\\${userId}'\\`;\n const user = await db.query(query);\n \n res.json(user);\n});\n\n// Attack example:\n// GET /api/users/1' OR '1'='1\n// Returns all users!\n\\`\\`\\`\n\n### The Solution\n\n#### 1. Use Parameterized Queries\n\n\\`\\`\\`javascript\n// ✅ Safe: Parameterized query\napp.get('/api/users/:id', async (req, res) => {\n const userId = req.params.id;\n \n // Validate input first\n if (!userId || !/^\\d+$/.test(userId)) {\n return res.status(400).json({ \n error: 'Invalid user ID' \n });\n }\n \n // Use parameterized query\n const user = await db.query(\n 'SELECT id, email, name FROM users WHERE id = $1',\n [userId]\n );\n \n if (!user) {\n return res.status(404).json({ \n error: 'User not found' \n });\n }\n \n res.json(user);\n});\n\\`\\`\\`\n\n#### 2. Use ORM with Proper Escaping\n\n\\`\\`\\`javascript\n// ✅ Safe: Using Prisma ORM\napp.get('/api/users/:id', async (req, res) => {\n const userId = parseInt(req.params.id);\n \n if (isNaN(userId)) {\n return res.status(400).json({ \n error: 'Invalid user ID' \n });\n }\n \n const user = await prisma.user.findUnique({\n where: { id: userId },\n select: {\n id: true,\n email: true,\n name: true,\n // Don't select sensitive fields\n }\n });\n \n if (!user) {\n return res.status(404).json({ \n error: 'User not found' \n });\n }\n \n res.json(user);\n});\n\\`\\`\\`\n\n#### 3. Implement Request Validation with Zod\n\n\\`\\`\\`javascript\nconst { z } = require('zod');\n\n// Define validation schema\nconst createUserSchema = z.object({\n email: z.string().email('Invalid email format'),\n password: z.string()\n .min(8, 'Password must be at least 8 characters')\n .regex(/[A-Z]/, 'Password must contain uppercase letter')\n .regex(/[a-z]/, 'Password must contain lowercase letter')\n .regex(/[0-9]/, 'Password must contain number'),\n name: z.string()\n .min(2, 'Name must be at least 2 characters')\n .max(100, 'Name too long'),\n age: z.number()\n .int('Age must be an integer')\n .min(18, 'Must be 18 or older')\n .max(120, 'Invalid age')\n .optional()\n});\n\n// Validation middleware\nfunction validateRequest(schema) {\n return (req, res, next) => {\n try {\n schema.parse(req.body);\n next();\n } catch (error) {\n res.status(400).json({\n error: 'Validation failed',\n details: error.errors\n });\n }\n };\n}\n\n// Use validation\napp.post('/api/users', \n validateRequest(createUserSchema),\n async (req, res) => {\n // Input is validated at this point\n const { email, password, name, age } = req.body;\n \n // Hash password\n const passwordHash = await bcrypt.hash(password, 10);\n \n // Create user\n const user = await prisma.user.create({\n data: {\n email,\n passwordHash,\n name,\n age\n }\n });\n \n // Don't return password hash\n const { passwordHash: _, ...userWithoutPassword } = user;\n res.status(201).json(userWithoutPassword);\n }\n);\n\\`\\`\\`\n\n#### 4. Sanitize Output to Prevent XSS\n\n\\`\\`\\`javascript\nconst DOMPurify = require('isomorphic-dompurify');\n\napp.post('/api/comments', authenticateToken, async (req, res) => {\n const { content } = req.body;\n \n // Validate\n if (!content || content.length > 1000) {\n return res.status(400).json({ \n error: 'Invalid comment content' \n });\n }\n \n // Sanitize HTML to prevent XSS\n const sanitizedContent = DOMPurify.sanitize(content, {\n ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],\n ALLOWED_ATTR: ['href']\n });\n \n const comment = await prisma.comment.create({\n data: {\n content: sanitizedContent,\n userId: req.user.userId\n }\n });\n \n res.status(201).json(comment);\n});\n\\`\\`\\`\n\n### Validation Checklist\n\n- [ ] Validate all user inputs\n- [ ] Use parameterized queries or ORM\n- [ ] Validate data types (string, number, email, etc.)\n- [ ] Validate data ranges (min/max length, value ranges)\n- [ ] Sanitize HTML content\n- [ ] Escape special characters\n- [ ] Validate file uploads (type, size, content)\n- [ ] Use allowlists, not blocklists\n```\n\n\n### Example 3: Rate Limiting and DDoS Protection\n\n```markdown\n## Implementing Rate Limiting\n\n### Why Rate Limiting?\n\n- Prevent brute force attacks\n- Protect against DDoS\n- Prevent API abuse\n- Ensure fair usage\n- Reduce server costs\n\n### Implementation with Express Rate Limit\n\n\\`\\`\\`javascript\nconst rateLimit = require('express-rate-limit');\nconst RedisStore = require('rate-limit-redis');\nconst Redis = require('ioredis');\n\n// Create Redis client\nconst redis = new Redis({\n host: process.env.REDIS_HOST,\n port: process.env.REDIS_PORT\n});\n\n// General API rate limit\nconst apiLimiter = rateLimit({\n store: new RedisStore({\n client: redis,\n prefix: 'rl:api:'\n }),\n windowMs: 15 * 60 * 1000, // 15 minutes\n max: 100, // 100 requests per window\n message: {\n error: 'Too many requests, please try again later',\n retryAfter: 900 // seconds\n },\n standardHeaders: true, // Return rate limit info in headers\n legacyHeaders: false,\n // Custom key generator (by user ID or IP)\n keyGenerator: (req) => {\n return req.user?.userId || req.ip;\n }\n});\n\n// Strict rate limit for authentication endpoints\nconst authLimiter = rateLimit({\n store: new RedisStore({\n client: redis,\n prefix: 'rl:auth:'\n }),\n windowMs: 15 * 60 * 1000, // 15 minutes\n max: 5, // Only 5 login attempts per 15 minutes\n skipSuccessfulRequests: true, // Don't count successful logins\n message: {\n error: 'Too many login attempts, please try again later',\n retryAfter: 900\n }\n});\n\n// Apply rate limiters\napp.use('/api/', apiLimiter);\napp.use('/api/auth/login', authLimiter);\napp.use('/api/auth/register', authLimiter);\n\n// Custom rate limiter for expensive operations\nconst expensiveLimiter = rateLimit({\n windowMs: 60 * 60 * 1000, // 1 hour\n max: 10, // 10 requests per hour\n message: {\n error: 'Rate limit exceeded for this operation'\n }\n});\n\napp.post('/api/reports/generate', \n authenticateToken,\n expensiveLimiter,\n async (req, res) => {\n // Expensive operation\n }\n);\n\\`\\`\\`\n\n### Advanced: Per-User Rate Limiting\n\n\\`\\`\\`javascript\n// Different limits based on user tier\nfunction createTieredRateLimiter() {\n const limits = {\n free: { windowMs: 60 * 60 * 1000, max: 100 },\n pro: { windowMs: 60 * 60 * 1000, max: 1000 },\n enterprise: { windowMs: 60 * 60 * 1000, max: 10000 }\n };\n \n return async (req, res, next) => {\n const user = req.user;\n const tier = user?.tier || 'free';\n const limit = limits[tier];\n \n const key = \\`rl:user:\\${user.userId}\\`;\n const current = await redis.incr(key);\n \n if (current === 1) {\n await redis.expire(key, limit.windowMs / 1000);\n }\n \n if (current > limit.max) {\n return res.status(429).json({\n error: 'Rate limit exceeded',\n limit: limit.max,\n remaining: 0,\n reset: await redis.ttl(key)\n });\n }\n \n // Set rate limit headers\n res.set({\n 'X-RateLimit-Limit': limit.max,\n 'X-RateLimit-Remaining': limit.max - current,\n 'X-RateLimit-Reset': await redis.ttl(key)\n });\n \n next();\n };\n}\n\napp.use('/api/', authenticateToken, createTieredRateLimiter());\n\\`\\`\\`\n\n### DDoS Protection with Helmet\n\n\\`\\`\\`javascript\nconst helmet = require('helmet');\n\napp.use(helmet({\n // Content Security Policy\n contentSecurityPolicy: {\n directives: {\n defaultSrc: [\"'self'\"],\n styleSrc: [\"'self'\", \"'unsafe-inline'\"],\n scriptSrc: [\"'self'\"],\n imgSrc: [\"'self'\", 'data:', 'https:']\n }\n },\n // Prevent clickjacking\n frameguard: { action: 'deny' },\n // Hide X-Powered-By header\n hidePoweredBy: true,\n // Prevent MIME type sniffing\n noSniff: true,\n // Enable HSTS\n hsts: {\n maxAge: 31536000,\n includeSubDomains: true,\n preload: true\n }\n}));\n\\`\\`\\`\n\n### Rate Limit Response Headers\n\n\\`\\`\\`\nX-RateLimit-Limit: 100\nX-RateLimit-Remaining: 87\nX-RateLimit-Reset: 1640000000\nRetry-After: 900\n\\`\\`\\`\n```\n\n## Best Practices\n\n### ✅ Do This\n\n- **Use HTTPS Everywhere** - Never send sensitive data over HTTP\n- **Implement Authentication** - Require authentication for protected endpoints\n- **Validate All Inputs** - Never trust user input\n- **Use Parameterized Queries** - Prevent SQL injection\n- **Implement Rate Limiting** - Protect against brute force and DDoS\n- **Hash Passwords** - Use bcrypt with salt rounds >= 10\n- **Use Short-Lived Tokens** - JWT access tokens should expire quickly\n- **Implement CORS Properly** - Only allow trusted origins\n- **Log Security Events** - Monitor for suspicious activity\n- **Keep Dependencies Updated** - Regularly update packages\n- **Use Security Headers** - Implement Helmet.js\n- **Sanitize Error Messages** - Don't leak sensitive information\n\n### ❌ Don't Do This\n\n- **Don't Store Passwords in Plain Text** - Always hash passwords\n- **Don't Use Weak Secrets** - Use strong, random JWT secrets\n- **Don't Trust User Input** - Always validate and sanitize\n- **Don't Expose Stack Traces** - Hide error details in production\n- **Don't Use String Concatenation for SQL** - Use parameterized queries\n- **Don't Store Sensitive Data in JWT** - JWTs are not encrypted\n- **Don't Ignore Security Updates** - Update dependencies regularly\n- **Don't Use Default Credentials** - Change all default passwords\n- **Don't Disable CORS Completely** - Configure it properly instead\n- **Don't Log Sensitive Data** - Sanitize logs\n\n## Common Pitfalls\n\n### Problem: JWT Secret Exposed in Code\n**Symptoms:** JWT secret hardcoded or committed to Git\n**Solution:**\n\\`\\`\\`javascript\n// ❌ Bad\nconst JWT_SECRET = 'my-secret-key';\n\n// ✅ Good\nconst JWT_SECRET = process.env.JWT_SECRET;\nif (!JWT_SECRET) {\n throw new Error('JWT_SECRET environment variable is required');\n}\n\n// Generate strong secret\n// node -e \"console.log(require('crypto').randomBytes(64).toString('hex'))\"\n\\`\\`\\`\n\n### Problem: Weak Password Requirements\n**Symptoms:** Users can set weak passwords like \"password123\"\n**Solution:**\n\\`\\`\\`javascript\nconst passwordSchema = z.string()\n .min(12, 'Password must be at least 12 characters')\n .regex(/[A-Z]/, 'Must contain uppercase letter')\n .regex(/[a-z]/, 'Must contain lowercase letter')\n .regex(/[0-9]/, 'Must contain number')\n .regex(/[^A-Za-z0-9]/, 'Must contain special character');\n\n// Or use a password strength library\nconst zxcvbn = require('zxcvbn');\nconst result = zxcvbn(password);\nif (result.score < 3) {\n return res.status(400).json({\n error: 'Password too weak',\n suggestions: result.feedback.suggestions\n });\n}\n\\`\\`\\`\n\n### Problem: Missing Authorization Checks\n**Symptoms:** Users can access resources they shouldn't\n**Solution:**\n\\`\\`\\`javascript\n// ❌ Bad: Only checks authentication\napp.delete('/api/posts/:id', authenticateToken, async (req, res) => {\n await prisma.post.delete({ where: { id: req.params.id } });\n res.json({ success: true });\n});\n\n// ✅ Good: Checks both authentication and authorization\napp.delete('/api/posts/:id', authenticateToken, async (req, res) => {\n const post = await prisma.post.findUnique({\n where: { id: req.params.id }\n });\n \n if (!post) {\n return res.status(404).json({ error: 'Post not found' });\n }\n \n // Check if user owns the post or is admin\n if (post.userId !== req.user.userId && req.user.role !== 'admin') {\n return res.status(403).json({ \n error: 'Not authorized to delete this post' \n });\n }\n \n await prisma.post.delete({ where: { id: req.params.id } });\n res.json({ success: true });\n});\n\\`\\`\\`\n\n### Problem: Verbose Error Messages\n**Symptoms:** Error messages reveal system details\n**Solution:**\n\\`\\`\\`javascript\n// ❌ Bad: Exposes database details\napp.post('/api/users', async (req, res) => {\n try {\n const user = await prisma.user.create({ data: req.body });\n res.json(user);\n } catch (error) {\n res.status(500).json({ error: error.message });\n // Error: \"Unique constraint failed on the fields: (`email`)\"\n }\n});\n\n// ✅ Good: Generic error message\napp.post('/api/users', async (req, res) => {\n try {\n const user = await prisma.user.create({ data: req.body });\n res.json(user);\n } catch (error) {\n console.error('User creation error:', error); // Log full error\n \n if (error.code === 'P2002') {\n return res.status(400).json({ \n error: 'Email already exists' \n });\n }\n \n res.status(500).json({ \n error: 'An error occurred while creating user' \n });\n }\n});\n\\`\\`\\`\n\n## Security Checklist\n\n### Authentication & Authorization\n- [ ] Implement strong authentication (JWT, OAuth 2.0)\n- [ ] Use HTTPS for all endpoints\n- [ ] Hash passwords with bcrypt (salt rounds >= 10)\n- [ ] Implement token expiration\n- [ ] Add refresh token mechanism\n- [ ] Verify user authorization for each request\n- [ ] Implement role-based access control (RBAC)\n\n### Input Validation\n- [ ] Validate all user inputs\n- [ ] Use parameterized queries or ORM\n- [ ] Sanitize HTML content\n- [ ] Validate file uploads\n- [ ] Implement request schema validation\n- [ ] Use allowlists, not blocklists\n\n### Rate Limiting & DDoS Protection\n- [ ] Implement rate limiting per user/IP\n- [ ] Add stricter limits for auth endpoints\n- [ ] Use Redis for distributed rate limiting\n- [ ] Return proper rate limit headers\n- [ ] Implement request throttling\n\n### Data Protection\n- [ ] Use HTTPS/TLS for all traffic\n- [ ] Encrypt sensitive data at rest\n- [ ] Don't store sensitive data in JWT\n- [ ] Sanitize error messages\n- [ ] Implement proper CORS configuration\n- [ ] Use security headers (Helmet.js)\n\n### Monitoring & Logging\n- [ ] Log security events\n- [ ] Monitor for suspicious activity\n- [ ] Set up alerts for failed auth attempts\n- [ ] Track API usage patterns\n- [ ] Don't log sensitive data\n\n## OWASP API Security Top 10\n\n1. **Broken Object Level Authorization** - Always verify user can access resource\n2. **Broken Authentication** - Implement strong authentication mechanisms\n3. **Broken Object Property Level Authorization** - Validate which properties user can access\n4. **Unrestricted Resource Consumption** - Implement rate limiting and quotas\n5. **Broken Function Level Authorization** - Verify user role for each function\n6. **Unrestricted Access to Sensitive Business Flows** - Protect critical workflows\n7. **Server Side Request Forgery (SSRF)** - Validate and sanitize URLs\n8. **Security Misconfiguration** - Use security best practices and headers\n9. **Improper Inventory Management** - Document and secure all API endpoints\n10. **Unsafe Consumption of APIs** - Validate data from third-party APIs\n\n## Related Skills\n\n- `@ethical-hacking-methodology` - Security testing perspective\n- `@sql-injection-testing` - Testing for SQL injection\n- `@xss-html-injection` - Testing for XSS vulnerabilities\n- `@broken-authentication` - Authentication vulnerabilities\n- `@backend-dev-guidelines` - Backend development standards\n- `@systematic-debugging` - Debug security issues\n\n## Additional Resources\n\n- [OWASP API Security Top 10](https://owasp.org/www-project-api-security/)\n- [JWT Best Practices](https://tools.ietf.org/html/rfc8725)\n- [Express Security Best Practices](https://expressjs.com/en/advanced/best-practice-security.html)\n- [Node.js Security Checklist](https://blog.risingstack.com/node-js-security-checklist/)\n- [API Security Checklist](https://github.com/shieldfy/API-Security-Checklist)\n\n---\n\n**Pro Tip:** Security is not a one-time task - regularly audit your APIs, keep dependencies updated, and stay informed about new vulnerabilities!\n"} |