Claude Code Plugins

Community-maintained marketplace

Feedback

Comprehensive security checklist covering OWASP Top 10, SQL injection, XSS, CSRF, authentication, authorization, secrets management, input validation, and security headers. Use when scanning for vulnerabilities, reviewing security, implementing authentication/authorization, or handling sensitive data.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name security-checklist
description Comprehensive security checklist covering OWASP Top 10, SQL injection, XSS, CSRF, authentication, authorization, secrets management, input validation, and security headers. Use when scanning for vulnerabilities, reviewing security, implementing authentication/authorization, or handling sensitive data.

Security Checklist

This skill provides comprehensive security guidance to protect your applications from common vulnerabilities and attacks.

OWASP Top 10 Vulnerabilities

1. Broken Access Control

What it is: Users can access resources or perform actions they shouldn't be authorized for.

Examples:

  • Accessing another user's data by changing URL parameter
  • Elevating privileges (user → admin)
  • Bypassing authentication checks

Prevention:

// ❌ BAD - No authorization check
app.get('/api/users/:id', async (req, res) => {
  const user = await db.user.findUnique({ where: { id: req.params.id } });
  res.json(user);
});

// ✅ GOOD - Verify ownership or admin
app.get('/api/users/:id', authenticate, async (req, res) => {
  const requestedId = req.params.id;
  const currentUserId = req.user.id;
  const isAdmin = req.user.role === 'admin';

  if (requestedId !== currentUserId && !isAdmin) {
    return res.status(403).json({ error: 'Forbidden' });
  }

  const user = await db.user.findUnique({ where: { id: requestedId } });
  res.json(user);
});

Checklist:

  • Enforce least privilege (deny by default)
  • Verify user permissions on every request
  • Never trust user IDs from client
  • Log access control failures
  • Use centralized access control logic

2. Cryptographic Failures

What it is: Exposing sensitive data due to weak or missing encryption.

Examples:

  • Storing passwords in plain text
  • Using weak hashing algorithms (MD5, SHA1)
  • Transmitting sensitive data over HTTP

Prevention:

import bcrypt from 'bcrypt';
import crypto from 'crypto';

// ✅ Password hashing
async function hashPassword(password: string): Promise<string> {
  const saltRounds = 12; // Increase for more security
  return await bcrypt.hash(password, saltRounds);
}

async function verifyPassword(password: string, hash: string): Promise<boolean> {
  return await bcrypt.compare(password, hash);
}

// ✅ Encrypt sensitive data at rest
function encrypt(text: string, key: string): string {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv('aes-256-gcm', Buffer.from(key), iv);

  let encrypted = cipher.update(text, 'utf8', 'hex');
  encrypted += cipher.final('hex');

  const authTag = cipher.getAuthTag().toString('hex');
  return `${iv.toString('hex')}:${authTag}:${encrypted}`;
}

Checklist:

  • Use HTTPS everywhere
  • Hash passwords with bcrypt (12+ rounds)
  • Encrypt sensitive data at rest
  • Use strong encryption algorithms (AES-256)
  • Properly manage encryption keys
  • Never commit secrets to version control

3. Injection Attacks

See SQL Injection Prevention section below

4. Insecure Design

What it is: Security flaws in the application architecture.

Examples:

  • Missing rate limiting
  • Unrestricted file uploads
  • Insecure password reset flows

Prevention:

// ✅ Rate limiting
import rateLimit from 'express-rate-limit';

const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // 5 attempts
  message: 'Too many login attempts, please try again later',
});

app.post('/api/auth/login', loginLimiter, async (req, res) => {
  // Login logic
});

// ✅ Secure file upload
import multer from 'multer';

const upload = multer({
  limits: {
    fileSize: 5 * 1024 * 1024, // 5MB max
  },
  fileFilter: (req, file, cb) => {
    const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
    if (!allowedTypes.includes(file.mimetype)) {
      return cb(new Error('Invalid file type'));
    }
    cb(null, true);
  },
});

Checklist:

  • Implement rate limiting on all endpoints
  • Validate file uploads (type, size, content)
  • Use secure password reset flows (token-based)
  • Implement account lockout after failed attempts
  • Log and monitor security events

5. Security Misconfiguration

What it is: Insecure default configurations, unnecessary features enabled.

Prevention:

// ✅ Secure Express configuration
import express from 'express';
import helmet from 'helmet';

const app = express();

// Security headers
app.use(helmet());

// Disable X-Powered-By
app.disable('x-powered-by');

// Environment-specific settings
if (process.env.NODE_ENV === 'production') {
  app.set('trust proxy', 1); // Trust first proxy
}

// CORS configuration
import cors from 'cors';

const corsOptions = {
  origin: process.env.ALLOWED_ORIGINS?.split(',') || [],
  credentials: true,
  optionsSuccessStatus: 200,
};

app.use(cors(corsOptions));

Checklist:

  • Remove default accounts and passwords
  • Disable directory listing
  • Remove unnecessary features/endpoints
  • Keep all software updated
  • Use security headers (see section below)

6. Vulnerable and Outdated Components

Prevention:

# Check for vulnerabilities
npm audit

# Fix vulnerabilities
npm audit fix

# Update dependencies
npm update

# Use automated tools
npm install -g snyk
snyk test
snyk monitor

Checklist:

  • Regularly update dependencies
  • Monitor security advisories
  • Remove unused dependencies
  • Use dependabot or renovate
  • Pin dependency versions in production

7. Identification and Authentication Failures

See Authentication Best Practices section below

8. Software and Data Integrity Failures

Prevention:

// ✅ Verify package integrity
// package-lock.json ensures same versions

// ✅ Use Subresource Integrity (SRI) for CDN
<script
  src="https://cdn.example.com/library.js"
  integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
  crossorigin="anonymous"
></script>

// ✅ Validate data from external sources
import { z } from 'zod';

const ExternalDataSchema = z.object({
  id: z.string().uuid(),
  amount: z.number().positive(),
  status: z.enum(['pending', 'completed', 'failed']),
});

function processExternalData(data: unknown) {
  const validated = ExternalDataSchema.parse(data);
  // Now safe to use
}

9. Security Logging and Monitoring Failures

Prevention:

import winston from 'winston';

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' }),
  ],
});

// ✅ Log security events
logger.warn('Failed login attempt', {
  email: req.body.email,
  ip: req.ip,
  userAgent: req.get('user-agent'),
  timestamp: new Date().toISOString(),
});

logger.error('SQL injection attempt detected', {
  query: sanitizedQuery,
  ip: req.ip,
  endpoint: req.path,
});

Checklist:

  • Log authentication events (login, logout, failures)
  • Log authorization failures
  • Log input validation failures
  • Monitor for suspicious patterns
  • Set up alerts for security events

10. Server-Side Request Forgery (SSRF)

Prevention:

import validator from 'validator';

// ❌ BAD - Allows SSRF
app.post('/api/fetch-url', async (req, res) => {
  const url = req.body.url;
  const response = await fetch(url);
  res.json(await response.json());
});

// ✅ GOOD - Validate and whitelist
app.post('/api/fetch-url', async (req, res) => {
  const url = req.body.url;

  // Validate URL format
  if (!validator.isURL(url, { protocols: ['https'] })) {
    return res.status(400).json({ error: 'Invalid URL' });
  }

  // Whitelist allowed domains
  const allowedDomains = ['api.trusted-service.com'];
  const urlObj = new URL(url);

  if (!allowedDomains.includes(urlObj.hostname)) {
    return res.status(403).json({ error: 'Domain not allowed' });
  }

  // Block internal IPs
  const hostname = urlObj.hostname;
  if (
    hostname === 'localhost' ||
    hostname.startsWith('192.168.') ||
    hostname.startsWith('10.') ||
    hostname.startsWith('172.')
  ) {
    return res.status(403).json({ error: 'Internal IPs not allowed' });
  }

  const response = await fetch(url);
  res.json(await response.json());
});

SQL Injection Prevention

Parameterized Queries

// ❌ VULNERABLE - String concatenation
const email = req.body.email;
const query = `SELECT * FROM users WHERE email = '${email}'`;
// Attacker can input: ' OR '1'='1
// Resulting query: SELECT * FROM users WHERE email = '' OR '1'='1'

// ✅ SAFE - Parameterized query
const email = req.body.email;
const query = 'SELECT * FROM users WHERE email = ?';
const user = await db.execute(query, [email]);

// ✅ SAFE - ORM (Prisma)
const user = await prisma.user.findUnique({
  where: { email: req.body.email },
});

Input Validation

import { z } from 'zod';

const UserQuerySchema = z.object({
  email: z.string().email(),
  id: z.string().uuid().optional(),
  name: z.string().max(100).optional(),
});

app.get('/api/users', async (req, res) => {
  try {
    const params = UserQuerySchema.parse(req.query);
    const users = await db.user.findMany({ where: params });
    res.json(users);
  } catch (error) {
    res.status(400).json({ error: 'Invalid query parameters' });
  }
});

XSS (Cross-Site Scripting) Prevention

Types of XSS

1. Stored XSS: Malicious script stored in database 2. Reflected XSS: Malicious script in URL/input reflected back 3. DOM-based XSS: Script manipulates DOM directly

Prevention

// ✅ React automatically escapes by default
function Comment({ text }: { text: string }) {
  return <p>{text}</p>; // Safe - React escapes HTML
}

// ❌ DANGEROUS - dangerouslySetInnerHTML
function Comment({ html }: { html: string }) {
  return <p dangerouslySetInnerHTML={{ __html: html }} />; // UNSAFE
}

// ✅ Sanitize HTML before rendering
import DOMPurify from 'dompurify';

function Comment({ html }: { html: string }) {
  const clean = DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
    ALLOWED_ATTR: ['href'],
  });
  return <p dangerouslySetInnerHTML={{ __html: clean }} />;
}

// ✅ Server-side sanitization
import sanitizeHtml from 'sanitize-html';

app.post('/api/comments', async (req, res) => {
  const clean = sanitizeHtml(req.body.content, {
    allowedTags: ['b', 'i', 'em', 'strong', 'a'],
    allowedAttributes: {
      'a': ['href'],
    },
  });

  const comment = await db.comment.create({
    data: { content: clean },
  });

  res.json(comment);
});

Content Security Policy (CSP)

app.use(
  helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", 'https://trusted-cdn.com'],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", 'data:', 'https:'],
      connectSrc: ["'self'", 'https://api.example.com'],
      fontSrc: ["'self'", 'https://fonts.gstatic.com'],
      objectSrc: ["'none'"],
      upgradeInsecureRequests: [],
    },
  })
);

CSRF (Cross-Site Request Forgery) Protection

CSRF Token Pattern

import csrf from 'csurf';
import cookieParser from 'cookie-parser';

const csrfProtection = csrf({ cookie: true });

app.use(cookieParser());

// Get CSRF token
app.get('/api/csrf-token', csrfProtection, (req, res) => {
  res.json({ csrfToken: req.csrfToken() });
});

// Protect state-changing endpoints
app.post('/api/users', csrfProtection, async (req, res) => {
  // Token automatically verified by middleware
  const user = await createUser(req.body);
  res.json(user);
});

SameSite Cookies

// ✅ Set SameSite attribute
res.cookie('sessionId', token, {
  httpOnly: true,
  secure: true, // HTTPS only
  sameSite: 'strict', // or 'lax'
  maxAge: 24 * 60 * 60 * 1000, // 24 hours
});

Authentication Best Practices

Password Requirements

import { z } from 'zod';

const PasswordSchema = z
  .string()
  .min(12, 'Password must be at least 12 characters')
  .regex(/[A-Z]/, 'Password must contain uppercase letter')
  .regex(/[a-z]/, 'Password must contain lowercase letter')
  .regex(/[0-9]/, 'Password must contain number')
  .regex(/[^A-Za-z0-9]/, 'Password must contain special character');

// Check against common passwords
import { isCommonPassword } from 'common-passwords';

function validatePassword(password: string): boolean {
  if (isCommonPassword(password)) {
    throw new Error('Password is too common');
  }
  return true;
}

JWT Best Practices

import jwt from 'jsonwebtoken';

// ✅ Short-lived access tokens
function generateAccessToken(userId: string): string {
  return jwt.sign(
    { userId, type: 'access' },
    process.env.JWT_SECRET!,
    { expiresIn: '15m' } // Short expiry
  );
}

// ✅ Long-lived refresh tokens
function generateRefreshToken(userId: string): string {
  return jwt.sign(
    { userId, type: 'refresh' },
    process.env.JWT_REFRESH_SECRET!,
    { expiresIn: '7d' }
  );
}

// ✅ Verify token
function verifyAccessToken(token: string) {
  try {
    const payload = jwt.verify(token, process.env.JWT_SECRET!);
    if (payload.type !== 'access') {
      throw new Error('Invalid token type');
    }
    return payload;
  } catch (error) {
    throw new Error('Invalid token');
  }
}

// ✅ Blacklist tokens on logout
const tokenBlacklist = new Set<string>();

app.post('/api/auth/logout', authenticate, (req, res) => {
  const token = req.headers.authorization?.split(' ')[1];
  if (token) {
    tokenBlacklist.add(token);
  }
  res.json({ message: 'Logged out' });
});

Multi-Factor Authentication (MFA)

import speakeasy from 'speakeasy';
import QRCode from 'qrcode';

// Generate MFA secret
async function enableMFA(userId: string) {
  const secret = speakeasy.generateSecret({
    name: `MyApp (${user.email})`,
  });

  // Generate QR code
  const qrCode = await QRCode.toDataURL(secret.otpauth_url!);

  // Save secret to database (encrypted)
  await db.user.update({
    where: { id: userId },
    data: { mfaSecret: encrypt(secret.base32) },
  });

  return { secret: secret.base32, qrCode };
}

// Verify MFA token
function verifyMFAToken(secret: string, token: string): boolean {
  return speakeasy.totp.verify({
    secret,
    encoding: 'base32',
    token,
    window: 2, // Allow 2 time steps before/after
  });
}

Authorization Patterns

Role-Based Access Control (RBAC)

enum Role {
  USER = 'user',
  MODERATOR = 'moderator',
  ADMIN = 'admin',
}

const permissions = {
  [Role.USER]: ['read:own', 'write:own'],
  [Role.MODERATOR]: ['read:own', 'write:own', 'read:all', 'delete:others'],
  [Role.ADMIN]: ['*'], // All permissions
};

function authorize(requiredPermission: string) {
  return (req: Request, res: Response, next: NextFunction) => {
    const userRole = req.user.role;
    const userPermissions = permissions[userRole];

    if (!userPermissions.includes('*') && !userPermissions.includes(requiredPermission)) {
      return res.status(403).json({ error: 'Forbidden' });
    }

    next();
  };
}

// Usage
app.delete('/api/posts/:id', authenticate, authorize('delete:others'), async (req, res) => {
  await deletePost(req.params.id);
  res.json({ success: true });
});

Attribute-Based Access Control (ABAC)

interface AccessPolicy {
  subject: { role: string; department?: string };
  resource: { type: string; owner?: string };
  action: string;
  conditions?: Record<string, any>;
}

function checkAccess(
  user: User,
  resource: Resource,
  action: string
): boolean {
  // User can access own resources
  if (resource.ownerId === user.id) {
    return true;
  }

  // Admins can access everything
  if (user.role === 'admin') {
    return true;
  }

  // Department managers can access department resources
  if (
    user.role === 'manager' &&
    user.department === resource.department
  ) {
    return true;
  }

  return false;
}

Secrets Management

Environment Variables

// ✅ .env file (NOT committed to git)
DATABASE_URL=postgresql://user:password@localhost:5432/db
JWT_SECRET=super-secret-key-change-in-production
API_KEY=sk_live_abc123xyz

// ✅ .env.example (committed to git)
DATABASE_URL=
JWT_SECRET=
API_KEY=

// ✅ Load environment variables
import dotenv from 'dotenv';
dotenv.config();

// ✅ Validate required env vars
const requiredEnvVars = ['DATABASE_URL', 'JWT_SECRET', 'API_KEY'];

for (const envVar of requiredEnvVars) {
  if (!process.env[envVar]) {
    throw new Error(`Missing required environment variable: ${envVar}`);
  }
}

Key Vault Integration

// ✅ AWS Secrets Manager
import { SecretsManager } from '@aws-sdk/client-secrets-manager';

async function getSecret(secretName: string): Promise<string> {
  const client = new SecretsManager({ region: 'us-east-1' });
  const response = await client.getSecretValue({ SecretId: secretName });
  return response.SecretString!;
}

// ✅ Azure Key Vault
import { SecretClient } from '@azure/keyvault-secrets';

async function getAzureSecret(secretName: string): Promise<string> {
  const client = new SecretClient(vaultUrl, credential);
  const secret = await client.getSecret(secretName);
  return secret.value!;
}

Input Validation

Validation Schema

import { z } from 'zod';

const CreateUserSchema = z.object({
  email: z.string().email().max(255),
  password: z.string().min(12).max(100),
  name: z.string().min(1).max(100),
  age: z.number().int().min(18).max(120),
  phone: z.string().regex(/^\+?[1-9]\d{1,14}$/).optional(),
  website: z.string().url().optional(),
});

app.post('/api/users', async (req, res) => {
  try {
    const data = CreateUserSchema.parse(req.body);
    const user = await createUser(data);
    res.json(user);
  } catch (error) {
    if (error instanceof z.ZodError) {
      return res.status(400).json({
        error: 'Validation failed',
        details: error.errors,
      });
    }
    throw error;
  }
});

Sanitization

import validator from 'validator';

function sanitizeInput(input: string): string {
  // Trim whitespace
  let clean = input.trim();

  // Escape HTML entities
  clean = validator.escape(clean);

  // Remove null bytes
  clean = clean.replace(/\0/g, '');

  return clean;
}

Security Headers

Comprehensive Header Configuration

import helmet from 'helmet';

app.use(
  helmet({
    // Content Security Policy
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'", "'unsafe-inline'"],
        styleSrc: ["'self'", "'unsafe-inline'"],
        imgSrc: ["'self'", 'data:', 'https:'],
      },
    },

    // HTTP Strict Transport Security
    hsts: {
      maxAge: 31536000, // 1 year
      includeSubDomains: true,
      preload: true,
    },

    // X-Frame-Options
    frameguard: {
      action: 'deny', // or 'sameorigin'
    },

    // X-Content-Type-Options
    noSniff: true,

    // X-XSS-Protection
    xssFilter: true,

    // Referrer-Policy
    referrerPolicy: {
      policy: 'strict-origin-when-cross-origin',
    },
  })
);

// Additional custom headers
app.use((req, res, next) => {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('X-XSS-Protection', '1; mode=block');
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
  next();
});

Rate Limiting

Endpoint-Specific Rate Limits

import rateLimit from 'express-rate-limit';

// Strict limit for authentication
const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5,
  message: 'Too many authentication attempts',
  standardHeaders: true,
  legacyHeaders: false,
});

// Moderate limit for API endpoints
const apiLimiter = rateLimit({
  windowMs: 1 * 60 * 1000, // 1 minute
  max: 60,
  message: 'Too many requests',
});

// Apply rate limiters
app.post('/api/auth/login', authLimiter, loginHandler);
app.post('/api/auth/register', authLimiter, registerHandler);
app.use('/api', apiLimiter);

Logging Security Events

What NOT to Log

❌ Never log:

  • Passwords (plain or hashed)
  • Credit card numbers
  • Social Security numbers
  • API keys/secrets
  • Session tokens
  • Personally identifiable information (PII) in clear text
// ❌ BAD - Logs sensitive data
logger.info('User login', { email, password });

// ✅ GOOD - Logs non-sensitive data
logger.info('User login attempt', {
  email,
  ip: req.ip,
  userAgent: req.get('user-agent'),
  success: true,
});

Security Event Logging

enum SecurityEvent {
  LOGIN_SUCCESS = 'login_success',
  LOGIN_FAILURE = 'login_failure',
  PASSWORD_RESET = 'password_reset',
  ACCOUNT_LOCKED = 'account_locked',
  PERMISSION_DENIED = 'permission_denied',
  SUSPICIOUS_ACTIVITY = 'suspicious_activity',
}

function logSecurityEvent(
  event: SecurityEvent,
  userId: string | null,
  metadata: Record<string, any>
) {
  logger.warn({
    type: 'security',
    event,
    userId,
    timestamp: new Date().toISOString(),
    ip: metadata.ip,
    userAgent: metadata.userAgent,
    ...metadata,
  });
}

Dependency Scanning

Automated Security Scanning

# .github/workflows/security.yml
name: Security Scan

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 0 * * 0' # Weekly

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Run npm audit
        run: npm audit --audit-level=high

      - name: Run Snyk security scan
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'

Security Checklist Summary

Pre-Deployment Checklist

Authentication & Authorization:

  • Passwords hashed with bcrypt (12+ rounds)
  • JWT tokens have short expiry (15 minutes)
  • Refresh tokens properly implemented
  • MFA available for sensitive accounts
  • Authorization checks on all endpoints
  • Rate limiting on auth endpoints

Input Validation:

  • All inputs validated with schemas
  • SQL queries use parameterized statements
  • File uploads restricted (type, size)
  • HTML content sanitized
  • No eval() or Function() with user input

Security Headers:

  • HTTPS enforced
  • CSP configured
  • HSTS enabled
  • X-Frame-Options set
  • X-Content-Type-Options set

Data Protection:

  • Sensitive data encrypted at rest
  • Secrets stored in environment variables/vault
  • No secrets in version control
  • PII handled according to regulations
  • Database backups encrypted

Logging & Monitoring:

  • Security events logged
  • No sensitive data in logs
  • Failed auth attempts monitored
  • Unusual activity alerts configured
  • Log retention policy defined

Dependencies:

  • All dependencies up to date
  • No known vulnerabilities (npm audit)
  • Dependabot/Renovate configured
  • Unused dependencies removed

API Security:

  • CORS properly configured
  • CSRF protection enabled
  • Rate limiting implemented
  • API keys rotated regularly
  • Endpoints documented

When to Use This Skill

Use this skill when:

  • Conducting security audits
  • Implementing authentication/authorization
  • Reviewing code for vulnerabilities
  • Setting up new projects
  • Handling sensitive data
  • Responding to security incidents
  • Training team on security
  • Preparing for compliance audits
  • Deploying to production
  • Integrating third-party services

Remember: Security is not a one-time task but an ongoing process. Stay informed about new vulnerabilities, keep dependencies updated, and always assume malicious actors are trying to exploit your application.