{"id":"75f803d6-b2db-413a-a8fe-5a01bafba7f3","shortId":"UJARBv","kind":"skill","title":"api-security-best-practices","tagline":"Implement secure API design patterns including authentication, authorization, input validation, rate limiting, and protection against common API vulnerabilities","description":"# 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\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.","tags":["api","security","best","practices","antigravity","awesome","skills","sickn33","agent-skills","agentic-skills","ai-agent-skills","ai-agents"],"capabilities":["skill","source-sickn33","skill-api-security-best-practices","topic-agent-skills","topic-agentic-skills","topic-ai-agent-skills","topic-ai-agents","topic-ai-coding","topic-ai-workflows","topic-antigravity","topic-antigravity-skills","topic-claude-code","topic-claude-code-skills","topic-codex-cli","topic-codex-skills"],"categories":["antigravity-awesome-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/sickn33/antigravity-awesome-skills/api-security-best-practices","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add sickn33/antigravity-awesome-skills","source_repo":"https://github.com/sickn33/antigravity-awesome-skills","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 35034 github stars · SKILL.md body (23,462 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-25T12:50:19.993Z","embedding":null,"createdAt":"2026-04-18T20:26:33.006Z","updatedAt":"2026-04-25T12:50:19.993Z","lastSeenAt":"2026-04-25T12:50:19.993Z","tsv":"'-9':1022,1969 '/.test':866 '/api':1432,1595 '/api/auth/login':343,1435 '/api/auth/refresh':623 '/api/auth/register':1438 '/api/comments':1153 '/api/posts':2029,2050 '/api/reports/generate':1470 '/api/user/profile':583 '/api/users':802,852,917,1089,2123,2156 '/api/users/1':834 '/en/advanced/best-practice-security.html)':2522 '/html/rfc8725)':2515 '/middleware/auth':579 '/node-js-security-checklist/)':2528 '/shieldfy/api-security-checklist)':2534 '/www-project-api-security/)':2509 '0':1021,1565,1968 '1':120,282,293,325,514,733,836,837,843,891,1453,1545,2356 '10':273,1114,1456,1457,1727,2221,2355,2445,2506 '100':1039,1336,1337,1501,1663 '1000':465,1165,1332,1397,1452,1499,1506,1508,1513,1550 '10000':1515 '12':1943,1949 '120':1059 '15':1330,1333,1395,1398,1407 '1640000000':1673 '18':1052,1055 '1h':422,704 '2':161,299,486,776,905,1030,1036,2367 '2.0':135,2209 '201':1136,1203 '24':462 '256':726 '3':191,303,572,968,1248,1999,2374 '31536000':1650 '3600':470,709 '4':222,308,617,1139,2386 '400':359,870,931,1079,1168,2002,2184 '401':385,402,521,551,634 '403':558,675,713,2089 '404':897,960,2067 '429':1556 '5':252,313,1401,1403,2395 '500':478,612,2139,2191 '6':320,2406 '60':463,464,1331,1396,1450,1451,1497,1498,1504,1505,1511,1512 '64':1922 '7':461,2416 '7d':444 '8':994,1000,2426 '87':1668 '9':1978,2435 '900':1351,1427,1677 'a-z':1003,1012,1952,1960 'a-za-z0':1974 'abus':196,1270 'access':148,524,683,736,1734,2017,2239,2365,2385,2408 'action':1630 'activ':220,1752,2334 'add':2225,2276 'addit':2500 'admin':2081,2086 'advanc':1478 'age':1043,1046,1061,1105,1125 'alert':2337 'allow':1183,1190,1743 'allowlist':1244,2264 'alreadi':2188 'alway':1783,1801,2361 'api':2,8,22,24,34,58,68,74,85,93,115,136,207,253,271,1269,1315,1328,2343,2352,2443,2449,2456,2503,2529,2548 'api-security-best-practic':1 'apilimit':1319,1433 'app':426,430,536,540 'app.delete':2028,2049 'app.get':582,801,851,916 'app.post':342,622,1088,1152,1469,2122,2155 'app.use':1431,1434,1437,1594,1607 'appli':1428 'ask':2591 'async':344,585,624,804,854,919,1092,1155,1473,1517,2032,2053,2124,2157 'attach':563 'attack':86,168,199,831,1264 'attempt':1405,1421,2341 'attr':1191 'audienc':427,537,769 'audit':101,2546 'auth':1393,2280,2340 'auth.js':331 'authent':12,37,78,121,129,131,142,158,260,285,289,291,1381,1692,1694,2027,2046,2202,2206,2369,2372,2484,2485 'authenticatetoken':498,571,577,584,1154,1471,1596,2031,2052 'authhead':507,512 'authheader.split':513 'authlimit':1384,1436,1439 'author':13,38,80,122,262,509,2012,2048,2093,2203,2231,2360,2379,2399 'await':371,394,450,591,660,687,826,881,939,1111,1119,1195,1540,1546,1567,1590,2035,2058,2098,2130,2163 'b':1185 'backend':2488,2491 'backend-dev-guidelin':2487 'bad':1887,2024,2118 'base':141,147,1487,2238 'bcrypt':337,339,1723,2218 'bcrypt.compare':395 'bcrypt.hash':1112 'bearer':515 'best':4,26,720,1678,2431,2511,2518 'bit':727 'blacklist':772 'blocklist':1246,2266 'blog.risingstack.com':2527 'blog.risingstack.com/node-js-security-checklist/)':2526 'boundari':2599 'broken':2357,2368,2375,2396,2483 'broken-authent':2482 'brute':1262,1716 'build':32 'busi':2411 'catch':471,609,710,1076,2136,2169 'chang':1849 'charact':1001,1037,1236,1950,1982 'check':266,651,2013,2026,2044,2073 'checklist':1207,2201,2525,2531 'choos':130 'clarif':2593 'clear':2566 'clickjack':1628 'client':309,314,1303,1324,1389 'code':793,1876 'command':188 'comment':1172,1194,1205 'commit':1882 'common':21,46,268,1869 'complet':1857 'concaten':1819 'conduct':92 'configur':209,1858,2321 'console.error':473,2171 'console.log':1918 'const':332,336,348,369,392,410,435,493,506,510,576,589,627,644,658,685,692,807,816,824,857,879,922,937,975,982,1101,1109,1117,1131,1146,1158,1179,1193,1283,1290,1297,1304,1318,1383,1446,1493,1521,1524,1529,1533,1538,1603,1888,1896,1939,1989,1993,2056,2128,2161 'constraint':2145 'consumpt':2389,2447 'contain':1008,1017,1025,1956,1964,1971,1980 'content':1159,1163,1173,1182,1198,1233,1242,1609,2255 'content.length':1164 'contentsecuritypolici':1612 'control':149,2240 'cor':1740,1856,2320 'cost':1276 'count':1413 'cover':50 'creat':1115,1301,2198 'createtieredratelimit':1492,1597 'createuserschema':983,1091 'creation':2173 'credenti':298,302,389,406,1848 'criteria':2602 'critic':2414 'crypto':1920 'current':1539,1544,1552,1585 'custom':1363,1440 'd':865 'danger':810 'data':113,172,223,227,229,235,243,452,761,1121,1197,1218,1225,1625,1688,1829,1866,2132,2165,2296,2305,2312,2350,2451 'databas':449,657,750,2120 'date':459,670 'date.now':460 'db.query':827,882 'db.refreshtoken.create':451 'db.refreshtoken.findfirst':661 'db.user.findunique':372,592,688 'ddos':88,198,1252,1267,1598,1719,2269 'debug':2496,2497 'decod':645 'decoded.userid':666,691 'default':1847,1851 'defaultsrc':1614 'defin':979 'delet':2095 'deni':1631 'depend':1754,1842,2550 'describ':2570 'design':9,66 'detail':1084,1812,2115,2121 'dev':2489 'develop':30,2492 'differ':1485 'direct':813,1613 'disabl':1855 'distribut':2285 'document':2439 'dompurifi':1147,1151 'dompurify.sanitize':1181 'e':1917 'em':1187 'email':349,355,362,374,415,599,697,885,947,985,987,989,1102,1122,1222,2150,2187 'enabl':1646 'encrypt':228,233,1835,2303 'endpoint':69,341,1382,1697,2214,2281,2444 'ensur':1271 'enterpris':1509 'environ':1909,2582 'environment-specif':2581 'err':542,545 'err.name':547 'error':215,240,246,361,387,404,472,475,476,480,482,523,553,560,610,614,616,636,677,711,715,872,899,933,962,1077,1081,1170,1342,1417,1462,1558,1765,1811,1906,2004,2069,2091,2108,2111,2137,2141,2143,2153,2170,2174,2175,2178,2186,2193,2195,2316 'error.code':2180 'error.errors':1085 'error.message':2142 'escap':910,1234 'etc':89,1223 'ethic':2460 'ethical-hacking-methodolog':2459 'event':1748,2330 'everywher':1684 'exampl':280,281,775,832,1247 'exceed':1465,1561 'exist':73,382,655,2189 'expens':1444,1476 'expensivelimit':1447,1472 'expert':2587 'expir':555,731,1737,2224 'expiresat':457,667 'expiresin':421,443,469,703,708 'expos':1807,1874,2119 'express':1279,1287,2516 'express-rate-limit':1286 'expressjs.com':2521 'expressjs.com/en/advanced/best-practice-security.html)':2520 'factor':157 'fail':1083,2146,2339 'fair':1272 'fals':1362 'field':955,2149 'file':1238,2257 'find':367 'first':862 'flow':292,2412 'forc':1263,1717 'forgeri':2420 'format':990 'found':902,965,2072 'frameguard':1629 'free':1495,1528 'full':2177 'function':497,1065,1491,2397,2405 'general':1314 'generat':305,326,407,432,681,1365,1913 'generic':2152 'get':502,833 'git':1884 'github.com':2533 'github.com/shieldfy/api-security-checklist)':2532 'good':1895,2043,2151 'grace':216 'graphql':55 'gt':668 'guid':29 'guidelin':2490 'hack':2461 'handl':111,212,241,276 'hardcod':1880 'hash':1107,1130,1720,1784,2215 'header':250,505,1360,1573,1637,1658,1761,2292,2324,2434 'helmet':1601,1604,1606,1608 'helmet.js':1763,2325 'help':125 'hex':1924 'hide':1632,1810 'hidepoweredbi':1638 'host':1308,1310 'hour':734,1454,1460 'href':1192 'hsts':1647,1648 'html':1175,1232,2254,2476 'http':1690 'https':755,1626,1683,2211 'https/tls':232,2299 'id':594,597,690,803,822,853,875,884,890,918,936,942,945,1368,2030,2038,2051,2061,2101 'ignor':1838 'imgsrc':1623 'implement':6,36,77,104,127,138,154,179,200,238,258,283,290,324,618,738,770,969,1255,1277,1691,1711,1739,1762,2204,2222,2235,2259,2271,2293,2318,2370,2390 'improp':2436 'includ':11 'includesubdomain':1651 'info':1358 'inform':1771,2554 'inject':87,167,185,189,781,786,799,1710,2468,2473,2477 'inlin':1620 'input':14,39,162,171,175,275,353,777,788,812,861,1095,1211,1700,1704,1800,2242,2247,2596 'instead':1861 'int':1045 'integ':1050 'invalid':388,405,561,678,716,873,934,988,1060,1171 'inventori':2437 'ioredi':1300 'ip':1370 'isnan':927 'isomorph':1150 'isomorphic-dompurifi':1149 'issu':2499 'issuer':423,533,767 'javascript':330,491,575,621,794,847,911,974,1145,1282,1484,1602,1886,1938,2023,2117 'json':360,386,403,479,522,552,559,613,635,676,714,871,898,932,961,1080,1137,1169,1204,1557,2003,2068,2090,2140,2185,2192 'jsonwebtoken':335,496 'jwt':133,284,288,306,328,333,408,488,494,724,763,1733,1794,1831,1872,1878,1889,1897,1902,1907,2207,2314,2510 'jwt.sign':412,437,694 'jwt.verify':529,646 'jwts':1832 'keep':1753,2549 'key':137,1364,1534,1542,1548,1569,1592,1894 'keygener':1371 'later':1349,1425 'leak':244,1769 'least':999,1035,1948 'legacyhead':1361 'length':1228 'letter':1010,1019,1958,1966 'level':2359,2378,2398 'librari':1988 'like':1935 'limit':17,42,106,193,202,214,279,1250,1257,1260,1281,1289,1295,1317,1357,1379,1430,1442,1464,1483,1486,1494,1530,1531,1560,1562,1572,1578,1656,1662,1713,2268,2273,2278,2287,2291,2392,2558 'limit.max':1553,1563,1579,1584 'limit.windowms':1549 'live':744,1731 'll':124 'log':295,1746,1864,1868,2176,2327,2328,2348 'login':340,474,485,1404,1415,1420 'logout':774 'long':743,1042 'long-liv':742 'lowercas':1018,1965 'manag':153,2438 'mani':1344,1419 'markdown':286,783,1254 'match':2567 'max':1038,1058,1335,1400,1455,1500,1507,1514 'maxag':1649 'mechan':2228,2373 'messag':247,1341,1416,1461,1766,2109,2112,2154,2317 'method':132 'methodolog':2462 'mfa':159 'middlewar':490,1064 'middleware/auth.js':492 'mime':1641 'min':993,1029,1051,1942 'min/max':1227 'minimum':728 'minut':1334,1399,1408 'misconfigur':2428 'miss':2011,2604 'module.exports':570 'monitor':217,1749,2326,2331 'multi':156 'multi-factor':155 'must':996,1007,1016,1024,1032,1047,1053,1945,1955,1963,1970,1979 'my-secret-key':1891 'name':601,886,949,1027,1031,1040,1104,1124 'never':795,1685,1701 'new':67,458,669,682,1306,1322,1387,1905,2556 'newtoken':693,707 'next':501,569,1071,1075,1520,1593 'node':1916 'node.js':2523 'nosniff':1644 'number':1026,1221,1972 'oauth':134,2208 'object':2358,2376 'occur':483,2196 'older':1057 'one':2542 'one-tim':2541 'oper':1445,1468,1477 'option':1062 'origin':1745 'orm':907,915,1216,2252 'output':1141,2576 'overview':28 'owasp':270,2351,2502 'owasp.org':2508 'owasp.org/www-project-api-security/)':2507 'own':2076 'p2002':2181 'packag':1758 'parameter':177,845,849,877,1213,1706,1823,2249 'parseint':924 'parti':2455 'password':350,356,364,391,396,991,995,1006,1015,1023,1103,1108,1113,1129,1721,1779,1785,1852,1927,1934,1944,1986,1996,2005,2216 'password123':1936 'passwordhash':606,1110,1123,1132 'passwordschema':1940 'pattern':10,52,2345 'payload':764 'penetr':264 'per':203,1339,1406,1459,1480,2274 'per-us':1479 'perform':263 'permiss':2597 'perspect':2465 'pitfal':1870 'plain':1781 'pleas':1346,1422 'point':1100 'polici':1611 'port':1311,1313 'post':2057,2064,2070,2078,2097 'post.userid':2083 'power':1635 'practic':5,27,721,1679,2432,2512,2519 'prefix':1326,1391 'preload':1653 'prepar':98 'prevent':183,195,782,784,1143,1177,1261,1268,1627,1640,1708 'prisma':914 'prisma.comment.create':1196 'prisma.post.delete':2036,2099 'prisma.post.findunique':2059 'prisma.user.create':1120,2131,2164 'prisma.user.findunique':940 'pro':1502,2535 'problem':791,1871,1925,2010,2106 'process.env.jwt':419,440,531,648,701,1899 'process.env.redis':1309,1312 'product':1814 'proper':239,909,1741,1860,2289,2319 'properti':2377,2382 'protect':19,44,83,165,224,573,580,1253,1265,1599,1696,1714,2270,2297,2413 'queri':178,815,817,828,846,850,878,1214,1707,1824,2250 'quick':1738 'quota':211,2394 'random':1793 'randombyt':1921 'rang':1226,1230 'rate':16,41,105,192,201,213,278,1249,1256,1259,1280,1288,1294,1316,1356,1378,1429,1441,1463,1482,1559,1571,1655,1712,2267,2272,2286,2290,2391 'rate-limit-redi':1293 'ratelimit':1284,1320,1385,1448,1577,1582,1588,1661,1666,1671 'rbac':150,2241 'redi':1296,1298,1302,1305,1307,1325,1390,2283 'redis.expire':1547 'redis.incr':1541 'redis.ttl':1568,1591 'redisstor':1291,1323,1388 'reduc':1274 'refresh':433,441,446,620,637,642,649,653,679,717,739,747,2226 'refreshtoken':436,454,468,628,631,647,664 'regex':1002,1011,1020,1951,1959,1967,1973 'regular':1756,1843,2545 'relat':2457 'remain':1564,1583,1667 'req':345,499,586,625,805,855,920,1069,1093,1156,1372,1474,1518,2033,2054,2125,2158 'req.body':351,629,1074,1106,1160,2133,2166 'req.headers':508 'req.ip':1376 'req.params.id':809,859,925,2039,2062,2102 'req.user':567,1374,1523 'req.user.role':2085 'req.user.userid':595,1201,2084 'request':180,210,319,566,970,1338,1345,1458,2234,2260,2294,2419 'requir':334,338,366,495,526,578,639,977,1148,1285,1292,1299,1605,1693,1912,1919,1928,1991,2595 'res':346,500,587,626,806,856,921,1070,1094,1157,1475,1519,2034,2055,2126,2159 'res.json':466,607,705,829,903,966,2040,2103,2134,2167 'res.set':1574 'res.status':358,384,401,477,520,550,557,611,633,674,712,869,896,930,959,1078,1135,1167,1202,1555,2001,2066,2088,2138,2183,2190 'reset':1566,1589,1672 'resourc':2018,2366,2388,2501 'respons':1657 'rest':54,237,2307 'result':1994 'result.feedback.suggestions':2009 'result.score':1998 'retri':1675 'retry-aft':1674 'retryaft':1350,1426 'return':357,383,400,519,549,556,605,632,673,838,868,895,929,958,1068,1128,1166,1355,1373,1516,1554,2000,2065,2087,2182,2288 'reveal':379,2113 'review':95,2588 'revok':753 'rl':1327,1392,1535 'role':146,417,699,2237,2402 'role-bas':145,2236 'round':1726,2220 'rout':574,581 'safe':848,912 'safeti':2598 'salt':1725,2219 'sanit':164,173,245,1140,1174,1231,1764,1804,1867,2253,2315,2424 'sanitizedcont':1180,1199 'schema':181,981,1067,2261 'schema.parse':1073 'scope':2569 'scriptsrc':1621 'second':1352 'secret':420,442,532,650,702,725,1790,1795,1873,1879,1890,1893,1898,1900,1903,1908,1915 'secur':3,7,25,33,51,72,94,100,128,151,225,249,254,257,287,312,327,719,1610,1747,1760,1839,2200,2323,2329,2353,2427,2430,2441,2463,2498,2504,2517,2524,2530,2537 'select':596,818,883,944,953 'self':1615,1617,1622,1624 'send':315,1686 'sensit':112,226,234,760,954,1687,1770,1828,1865,2304,2311,2349,2410 'server':300,304,321,615,1275,2417 'session':152,745 'set':143,205,729,1570,1932,2335 'short':730,1730 'short-liv':1729 'shouldn':2020 'side':2418 'size':1241 'skill':49,63,2458,2561 'skill-api-security-best-practices' 'skipsuccessfulrequest':1409 'snif':1643 'solut':842,1885,1937,2022,2116 'source-sickn33' 'special':1235,1981 'specif':2583 'sql':184,780,785,798,1709,1821,2467,2472 'sql-injection-test':2466 'ssrf':2421 'stack':1808 'standard':2493 'standardhead':1353 'stay':2553 'step':119,160,190,221,251 'stop':2589 'store':310,445,746,759,1321,1386,1778,1827,2310 'storedtoken':659,672 'strength':1987 'strict':1377 'stricter':2277 'string':1220,1818 'strong':723,1188,1792,1914,2205,2371 'stylesrc':1616 'substitut':2579 'success':1414,2041,2104,2601 'suggest':2008 'suspici':219,1751,2333 'symptom':1877,1929,2014,2110 'system':2114 'systemat':2495 'systematic-debug':2494 'tag':1184 'task':2544,2565 'test':255,259,265,277,2464,2469,2470,2478,2585 'text':1782 'third':2454 'third-parti':2453 'throttl':108,194,208,2295 'throw':1904 'tier':1490,1525,1527,1532 'time':732,2543 'tip':2536 'token':140,307,311,316,323,329,409,411,434,447,453,467,489,503,511,516,518,525,528,530,554,562,619,638,643,654,663,680,684,706,718,737,740,748,766,771,1732,1735,2223,2227 'token-bas':139 'tokenexpirederror':548 'tools.ietf.org':2514 'tools.ietf.org/html/rfc8725)':2513 'top':272,2354,2505 'topic-agent-skills' 'topic-agentic-skills' 'topic-ai-agent-skills' 'topic-ai-agents' 'topic-ai-coding' 'topic-ai-workflows' 'topic-antigravity' 'topic-antigravity-skills' 'topic-claude-code' 'topic-claude-code-skills' 'topic-codex-cli' 'topic-codex-skills' 'tostr':1923 'trace':1809 'track':2342 'traffic':2302 'transit':231 'treat':2574 'tri':347,588,640,1072,1347,1423,2127,2160 'true':598,600,602,946,948,950,1354,1410,1639,1645,1652,1654,2042,2105 'trust':1702,1744,1798 'type':1219,1240,1642 'uniqu':2144 'unrestrict':2387,2407 'unsaf':1619,2446 'unsafe-inlin':1618 'updat':1755,1757,1840,1841,2551 'upload':1239,2258 'uppercas':1009,1957 'url':2425 'usag':1273,2344 'use':61,64,70,75,81,90,96,102,109,176,248,722,754,844,876,906,913,1086,1212,1243,1682,1705,1722,1728,1759,1788,1791,1817,1822,1846,1984,2210,2248,2263,2282,2298,2322,2429,2559 'user':174,294,368,370,376,381,431,541,543,564,568,590,608,686,811,820,825,830,840,874,880,888,894,900,904,935,938,957,963,967,1116,1118,1134,1210,1367,1481,1489,1522,1526,1536,1703,1799,1930,2015,2075,2129,2135,2162,2168,2172,2199,2230,2246,2363,2383,2401 'user.email':416,698 'user.id':414,439,456,696 'user.passwordhash':397 'user.role':418,700 'user.userid':1537 'user/ip':204,2275 'userid':413,438,455,665,695,808,823,858,864,867,892,923,928,943,1200,1375 'userwithoutpassword':1133,1138 'valid':15,40,163,169,182,274,301,322,352,765,778,789,860,971,980,1063,1082,1087,1097,1161,1206,1208,1217,1224,1237,1698,1802,2243,2244,2256,2262,2380,2422,2450,2584 'validaterequest':1066,1090 'validpassword':393,399 'valu':1229 'variabl':1910 'verbos':2107 'verifi':256,390,487,527,641,2229,2362,2400 'vulner':23,47,269,792,800,2481,2486,2557 'weak':1789,1926,1933,2007 'websocket':57 'window':1340 'windowm':1329,1394,1449,1496,1503,1510 'work':118 'workflow':2415 'x':1576,1581,1587,1634,1660,1665,1670 'x-powered-bi':1633 'x-ratelimit-limit':1575,1659 'x-ratelimit-remain':1580,1664 'x-ratelimit-reset':1586,1669 'xss':186,1144,1178,2475,2480 'xss-html-inject':2474 'your-app':424,534 'your-app-us':428,538 'z':976,1005,1014,1954,1962 'z.number':1044 'z.object':984 'z.string':986,992,1028,1941 'z0':1977 'za':1976 'zod':973,978 'zxcvbn':1990,1992,1995","prices":[{"id":"8941cbce-48bf-44c9-a86a-fb601625b56c","listingId":"75f803d6-b2db-413a-a8fe-5a01bafba7f3","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"sickn33","category":"antigravity-awesome-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T20:26:33.006Z"}],"sources":[{"listingId":"75f803d6-b2db-413a-a8fe-5a01bafba7f3","source":"github","sourceId":"sickn33/antigravity-awesome-skills/api-security-best-practices","sourceUrl":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/api-security-best-practices","isPrimary":false,"firstSeenAt":"2026-04-18T21:31:10.204Z","lastSeenAt":"2026-04-25T12:50:19.993Z"},{"listingId":"75f803d6-b2db-413a-a8fe-5a01bafba7f3","source":"skills_sh","sourceId":"sickn33/antigravity-awesome-skills/api-security-best-practices","sourceUrl":"https://skills.sh/sickn33/antigravity-awesome-skills/api-security-best-practices","isPrimary":true,"firstSeenAt":"2026-04-18T20:26:33.006Z","lastSeenAt":"2026-04-25T12:40:13.926Z"}],"details":{"listingId":"75f803d6-b2db-413a-a8fe-5a01bafba7f3","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"sickn33","slug":"api-security-best-practices","github":{"repo":"sickn33/antigravity-awesome-skills","stars":35034,"topics":["agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows","antigravity","antigravity-skills","claude-code","claude-code-skills","codex-cli","codex-skills","cursor","cursor-skills","developer-tools","gemini-cli","gemini-skills","kiro","mcp","skill-library"],"license":"mit","html_url":"https://github.com/sickn33/antigravity-awesome-skills","pushed_at":"2026-04-25T06:33:17Z","description":"Installable GitHub library of 1,400+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.","skill_md_sha":"3afee9a7deb40898dba1e523f418f3f8ca1e080b","skill_md_path":"skills/api-security-best-practices/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/api-security-best-practices"},"layout":"multi","source":"github","category":"antigravity-awesome-skills","frontmatter":{"name":"api-security-best-practices","description":"Implement secure API design patterns including authentication, authorization, input validation, rate limiting, and protection against common API vulnerabilities"},"skills_sh_url":"https://skills.sh/sickn33/antigravity-awesome-skills/api-security-best-practices"},"updatedAt":"2026-04-25T12:50:19.993Z"}}