feat(bundles): add editorial bundle plugins
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "antigravity-bundle-security-developer",
|
||||
"version": "8.10.0",
|
||||
"description": "Install the \"Security Developer\" editorial skill bundle from Antigravity Awesome Skills.",
|
||||
"author": {
|
||||
"name": "sickn33 and contributors",
|
||||
"url": "https://github.com/sickn33/antigravity-awesome-skills"
|
||||
},
|
||||
"homepage": "https://github.com/sickn33/antigravity-awesome-skills",
|
||||
"repository": "https://github.com/sickn33/antigravity-awesome-skills",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"codex",
|
||||
"skills",
|
||||
"bundle",
|
||||
"security-developer",
|
||||
"productivity"
|
||||
],
|
||||
"skills": "./skills/",
|
||||
"interface": {
|
||||
"displayName": "Security Developer",
|
||||
"shortDescription": "Security & Compliance · 6 curated skills",
|
||||
"longDescription": "For building secure applications. Covers API Security Best Practices, Auth Implementation Patterns, and 4 more skills.",
|
||||
"developerName": "sickn33 and contributors",
|
||||
"category": "Security & Compliance",
|
||||
"capabilities": [
|
||||
"Interactive",
|
||||
"Write"
|
||||
],
|
||||
"websiteURL": "https://github.com/sickn33/antigravity-awesome-skills",
|
||||
"brandColor": "#111827"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,910 @@
|
||||
---
|
||||
name: api-security-best-practices
|
||||
description: "Implement secure API design patterns including authentication, authorization, input validation, rate limiting, and protection against common API vulnerabilities"
|
||||
risk: unknown
|
||||
source: community
|
||||
date_added: "2026-02-27"
|
||||
---
|
||||
|
||||
# API Security Best Practices
|
||||
|
||||
## Overview
|
||||
|
||||
Guide 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.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Use when designing new API endpoints
|
||||
- Use when securing existing APIs
|
||||
- Use when implementing authentication and authorization
|
||||
- Use when protecting against API attacks (injection, DDoS, etc.)
|
||||
- Use when conducting API security reviews
|
||||
- Use when preparing for security audits
|
||||
- Use when implementing rate limiting and throttling
|
||||
- Use when handling sensitive data in APIs
|
||||
|
||||
## How It Works
|
||||
|
||||
### Step 1: Authentication & Authorization
|
||||
|
||||
I'll help you implement secure authentication:
|
||||
- Choose authentication method (JWT, OAuth 2.0, API keys)
|
||||
- Implement token-based authentication
|
||||
- Set up role-based access control (RBAC)
|
||||
- Secure session management
|
||||
- Implement multi-factor authentication (MFA)
|
||||
|
||||
### Step 2: Input Validation & Sanitization
|
||||
|
||||
Protect against injection attacks:
|
||||
- Validate all input data
|
||||
- Sanitize user inputs
|
||||
- Use parameterized queries
|
||||
- Implement request schema validation
|
||||
- Prevent SQL injection, XSS, and command injection
|
||||
|
||||
### Step 3: Rate Limiting & Throttling
|
||||
|
||||
Prevent abuse and DDoS attacks:
|
||||
- Implement rate limiting per user/IP
|
||||
- Set up API throttling
|
||||
- Configure request quotas
|
||||
- Handle rate limit errors gracefully
|
||||
- Monitor for suspicious activity
|
||||
|
||||
### Step 4: Data Protection
|
||||
|
||||
Secure sensitive data:
|
||||
- Encrypt data in transit (HTTPS/TLS)
|
||||
- Encrypt sensitive data at rest
|
||||
- Implement proper error handling (no data leaks)
|
||||
- Sanitize error messages
|
||||
- Use secure headers
|
||||
|
||||
### Step 5: API Security Testing
|
||||
|
||||
Verify security implementation:
|
||||
- Test authentication and authorization
|
||||
- Perform penetration testing
|
||||
- Check for common vulnerabilities (OWASP API Top 10)
|
||||
- Validate input handling
|
||||
- Test rate limiting
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Implementing JWT Authentication
|
||||
|
||||
```markdown
|
||||
## Secure JWT Authentication Implementation
|
||||
|
||||
### Authentication Flow
|
||||
|
||||
1. User logs in with credentials
|
||||
2. Server validates credentials
|
||||
3. Server generates JWT token
|
||||
4. Client stores token securely
|
||||
5. Client sends token with each request
|
||||
6. Server validates token
|
||||
|
||||
### Implementation
|
||||
|
||||
#### 1. Generate Secure JWT Tokens
|
||||
|
||||
\`\`\`javascript
|
||||
// auth.js
|
||||
const jwt = require('jsonwebtoken');
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
// Login endpoint
|
||||
app.post('/api/auth/login', async (req, res) => {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
|
||||
// Validate input
|
||||
if (!email || !password) {
|
||||
return res.status(400).json({
|
||||
error: 'Email and password are required'
|
||||
});
|
||||
}
|
||||
|
||||
// Find user
|
||||
const user = await db.user.findUnique({
|
||||
where: { email }
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
// Don't reveal if user exists
|
||||
return res.status(401).json({
|
||||
error: 'Invalid credentials'
|
||||
});
|
||||
}
|
||||
|
||||
// Verify password
|
||||
const validPassword = await bcrypt.compare(
|
||||
password,
|
||||
user.passwordHash
|
||||
);
|
||||
|
||||
if (!validPassword) {
|
||||
return res.status(401).json({
|
||||
error: 'Invalid credentials'
|
||||
});
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
const token = jwt.sign(
|
||||
{
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
role: user.role
|
||||
},
|
||||
process.env.JWT_SECRET,
|
||||
{
|
||||
expiresIn: '1h',
|
||||
issuer: 'your-app',
|
||||
audience: 'your-app-users'
|
||||
}
|
||||
);
|
||||
|
||||
// Generate refresh token
|
||||
const refreshToken = jwt.sign(
|
||||
{ userId: user.id },
|
||||
process.env.JWT_REFRESH_SECRET,
|
||||
{ expiresIn: '7d' }
|
||||
);
|
||||
|
||||
// Store refresh token in database
|
||||
await db.refreshToken.create({
|
||||
data: {
|
||||
token: refreshToken,
|
||||
userId: user.id,
|
||||
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
|
||||
}
|
||||
});
|
||||
|
||||
res.json({
|
||||
token,
|
||||
refreshToken,
|
||||
expiresIn: 3600
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Login error:', error);
|
||||
res.status(500).json({
|
||||
error: 'An error occurred during login'
|
||||
});
|
||||
}
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
#### 2. Verify JWT Tokens (Middleware)
|
||||
|
||||
\`\`\`javascript
|
||||
// middleware/auth.js
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
function authenticateToken(req, res, next) {
|
||||
// Get token from header
|
||||
const authHeader = req.headers['authorization'];
|
||||
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
|
||||
|
||||
if (!token) {
|
||||
return res.status(401).json({
|
||||
error: 'Access token required'
|
||||
});
|
||||
}
|
||||
|
||||
// Verify token
|
||||
jwt.verify(
|
||||
token,
|
||||
process.env.JWT_SECRET,
|
||||
{
|
||||
issuer: 'your-app',
|
||||
audience: 'your-app-users'
|
||||
},
|
||||
(err, user) => {
|
||||
if (err) {
|
||||
if (err.name === 'TokenExpiredError') {
|
||||
return res.status(401).json({
|
||||
error: 'Token expired'
|
||||
});
|
||||
}
|
||||
return res.status(403).json({
|
||||
error: 'Invalid token'
|
||||
});
|
||||
}
|
||||
|
||||
// Attach user to request
|
||||
req.user = user;
|
||||
next();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = { authenticateToken };
|
||||
\`\`\`
|
||||
|
||||
#### 3. Protect Routes
|
||||
|
||||
\`\`\`javascript
|
||||
const { authenticateToken } = require('./middleware/auth');
|
||||
|
||||
// Protected route
|
||||
app.get('/api/user/profile', authenticateToken, async (req, res) => {
|
||||
try {
|
||||
const user = await db.user.findUnique({
|
||||
where: { id: req.user.userId },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
// Don't return passwordHash
|
||||
}
|
||||
});
|
||||
|
||||
res.json(user);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Server error' });
|
||||
}
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
#### 4. Implement Token Refresh
|
||||
|
||||
\`\`\`javascript
|
||||
app.post('/api/auth/refresh', async (req, res) => {
|
||||
const { refreshToken } = req.body;
|
||||
|
||||
if (!refreshToken) {
|
||||
return res.status(401).json({
|
||||
error: 'Refresh token required'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Verify refresh token
|
||||
const decoded = jwt.verify(
|
||||
refreshToken,
|
||||
process.env.JWT_REFRESH_SECRET
|
||||
);
|
||||
|
||||
// Check if refresh token exists in database
|
||||
const storedToken = await db.refreshToken.findFirst({
|
||||
where: {
|
||||
token: refreshToken,
|
||||
userId: decoded.userId,
|
||||
expiresAt: { gt: new Date() }
|
||||
}
|
||||
});
|
||||
|
||||
if (!storedToken) {
|
||||
return res.status(403).json({
|
||||
error: 'Invalid refresh token'
|
||||
});
|
||||
}
|
||||
|
||||
// Generate new access token
|
||||
const user = await db.user.findUnique({
|
||||
where: { id: decoded.userId }
|
||||
});
|
||||
|
||||
const newToken = jwt.sign(
|
||||
{
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
role: user.role
|
||||
},
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: '1h' }
|
||||
);
|
||||
|
||||
res.json({
|
||||
token: newToken,
|
||||
expiresIn: 3600
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
res.status(403).json({
|
||||
error: 'Invalid refresh token'
|
||||
});
|
||||
}
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
### Security Best Practices
|
||||
|
||||
- ✅ Use strong JWT secrets (256-bit minimum)
|
||||
- ✅ Set short expiration times (1 hour for access tokens)
|
||||
- ✅ Implement refresh tokens for long-lived sessions
|
||||
- ✅ Store refresh tokens in database (can be revoked)
|
||||
- ✅ Use HTTPS only
|
||||
- ✅ Don't store sensitive data in JWT payload
|
||||
- ✅ Validate token issuer and audience
|
||||
- ✅ Implement token blacklisting for logout
|
||||
```
|
||||
|
||||
|
||||
### Example 2: Input Validation and SQL Injection Prevention
|
||||
|
||||
```markdown
|
||||
## Preventing SQL Injection and Input Validation
|
||||
|
||||
### The Problem
|
||||
|
||||
**❌ Vulnerable Code:**
|
||||
\`\`\`javascript
|
||||
// NEVER DO THIS - SQL Injection vulnerability
|
||||
app.get('/api/users/:id', async (req, res) => {
|
||||
const userId = req.params.id;
|
||||
|
||||
// Dangerous: User input directly in query
|
||||
const query = \`SELECT * FROM users WHERE id = '\${userId}'\`;
|
||||
const user = await db.query(query);
|
||||
|
||||
res.json(user);
|
||||
});
|
||||
|
||||
// Attack example:
|
||||
// GET /api/users/1' OR '1'='1
|
||||
// Returns all users!
|
||||
\`\`\`
|
||||
|
||||
### The Solution
|
||||
|
||||
#### 1. Use Parameterized Queries
|
||||
|
||||
\`\`\`javascript
|
||||
// ✅ Safe: Parameterized query
|
||||
app.get('/api/users/:id', async (req, res) => {
|
||||
const userId = req.params.id;
|
||||
|
||||
// Validate input first
|
||||
if (!userId || !/^\d+$/.test(userId)) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid user ID'
|
||||
});
|
||||
}
|
||||
|
||||
// Use parameterized query
|
||||
const user = await db.query(
|
||||
'SELECT id, email, name FROM users WHERE id = $1',
|
||||
[userId]
|
||||
);
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
error: 'User not found'
|
||||
});
|
||||
}
|
||||
|
||||
res.json(user);
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
#### 2. Use ORM with Proper Escaping
|
||||
|
||||
\`\`\`javascript
|
||||
// ✅ Safe: Using Prisma ORM
|
||||
app.get('/api/users/:id', async (req, res) => {
|
||||
const userId = parseInt(req.params.id);
|
||||
|
||||
if (isNaN(userId)) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid user ID'
|
||||
});
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
// Don't select sensitive fields
|
||||
}
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
error: 'User not found'
|
||||
});
|
||||
}
|
||||
|
||||
res.json(user);
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
#### 3. Implement Request Validation with Zod
|
||||
|
||||
\`\`\`javascript
|
||||
const { z } = require('zod');
|
||||
|
||||
// Define validation schema
|
||||
const createUserSchema = z.object({
|
||||
email: z.string().email('Invalid email format'),
|
||||
password: z.string()
|
||||
.min(8, 'Password must be at least 8 characters')
|
||||
.regex(/[A-Z]/, 'Password must contain uppercase letter')
|
||||
.regex(/[a-z]/, 'Password must contain lowercase letter')
|
||||
.regex(/[0-9]/, 'Password must contain number'),
|
||||
name: z.string()
|
||||
.min(2, 'Name must be at least 2 characters')
|
||||
.max(100, 'Name too long'),
|
||||
age: z.number()
|
||||
.int('Age must be an integer')
|
||||
.min(18, 'Must be 18 or older')
|
||||
.max(120, 'Invalid age')
|
||||
.optional()
|
||||
});
|
||||
|
||||
// Validation middleware
|
||||
function validateRequest(schema) {
|
||||
return (req, res, next) => {
|
||||
try {
|
||||
schema.parse(req.body);
|
||||
next();
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
error: 'Validation failed',
|
||||
details: error.errors
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Use validation
|
||||
app.post('/api/users',
|
||||
validateRequest(createUserSchema),
|
||||
async (req, res) => {
|
||||
// Input is validated at this point
|
||||
const { email, password, name, age } = req.body;
|
||||
|
||||
// Hash password
|
||||
const passwordHash = await bcrypt.hash(password, 10);
|
||||
|
||||
// Create user
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
email,
|
||||
passwordHash,
|
||||
name,
|
||||
age
|
||||
}
|
||||
});
|
||||
|
||||
// Don't return password hash
|
||||
const { passwordHash: _, ...userWithoutPassword } = user;
|
||||
res.status(201).json(userWithoutPassword);
|
||||
}
|
||||
);
|
||||
\`\`\`
|
||||
|
||||
#### 4. Sanitize Output to Prevent XSS
|
||||
|
||||
\`\`\`javascript
|
||||
const DOMPurify = require('isomorphic-dompurify');
|
||||
|
||||
app.post('/api/comments', authenticateToken, async (req, res) => {
|
||||
const { content } = req.body;
|
||||
|
||||
// Validate
|
||||
if (!content || content.length > 1000) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid comment content'
|
||||
});
|
||||
}
|
||||
|
||||
// Sanitize HTML to prevent XSS
|
||||
const sanitizedContent = DOMPurify.sanitize(content, {
|
||||
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
|
||||
ALLOWED_ATTR: ['href']
|
||||
});
|
||||
|
||||
const comment = await prisma.comment.create({
|
||||
data: {
|
||||
content: sanitizedContent,
|
||||
userId: req.user.userId
|
||||
}
|
||||
});
|
||||
|
||||
res.status(201).json(comment);
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
### Validation Checklist
|
||||
|
||||
- [ ] Validate all user inputs
|
||||
- [ ] Use parameterized queries or ORM
|
||||
- [ ] Validate data types (string, number, email, etc.)
|
||||
- [ ] Validate data ranges (min/max length, value ranges)
|
||||
- [ ] Sanitize HTML content
|
||||
- [ ] Escape special characters
|
||||
- [ ] Validate file uploads (type, size, content)
|
||||
- [ ] Use allowlists, not blocklists
|
||||
```
|
||||
|
||||
|
||||
### Example 3: Rate Limiting and DDoS Protection
|
||||
|
||||
```markdown
|
||||
## Implementing Rate Limiting
|
||||
|
||||
### Why Rate Limiting?
|
||||
|
||||
- Prevent brute force attacks
|
||||
- Protect against DDoS
|
||||
- Prevent API abuse
|
||||
- Ensure fair usage
|
||||
- Reduce server costs
|
||||
|
||||
### Implementation with Express Rate Limit
|
||||
|
||||
\`\`\`javascript
|
||||
const rateLimit = require('express-rate-limit');
|
||||
const RedisStore = require('rate-limit-redis');
|
||||
const Redis = require('ioredis');
|
||||
|
||||
// Create Redis client
|
||||
const redis = new Redis({
|
||||
host: process.env.REDIS_HOST,
|
||||
port: process.env.REDIS_PORT
|
||||
});
|
||||
|
||||
// General API rate limit
|
||||
const apiLimiter = rateLimit({
|
||||
store: new RedisStore({
|
||||
client: redis,
|
||||
prefix: 'rl:api:'
|
||||
}),
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 100, // 100 requests per window
|
||||
message: {
|
||||
error: 'Too many requests, please try again later',
|
||||
retryAfter: 900 // seconds
|
||||
},
|
||||
standardHeaders: true, // Return rate limit info in headers
|
||||
legacyHeaders: false,
|
||||
// Custom key generator (by user ID or IP)
|
||||
keyGenerator: (req) => {
|
||||
return req.user?.userId || req.ip;
|
||||
}
|
||||
});
|
||||
|
||||
// Strict rate limit for authentication endpoints
|
||||
const authLimiter = rateLimit({
|
||||
store: new RedisStore({
|
||||
client: redis,
|
||||
prefix: 'rl:auth:'
|
||||
}),
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 5, // Only 5 login attempts per 15 minutes
|
||||
skipSuccessfulRequests: true, // Don't count successful logins
|
||||
message: {
|
||||
error: 'Too many login attempts, please try again later',
|
||||
retryAfter: 900
|
||||
}
|
||||
});
|
||||
|
||||
// Apply rate limiters
|
||||
app.use('/api/', apiLimiter);
|
||||
app.use('/api/auth/login', authLimiter);
|
||||
app.use('/api/auth/register', authLimiter);
|
||||
|
||||
// Custom rate limiter for expensive operations
|
||||
const expensiveLimiter = rateLimit({
|
||||
windowMs: 60 * 60 * 1000, // 1 hour
|
||||
max: 10, // 10 requests per hour
|
||||
message: {
|
||||
error: 'Rate limit exceeded for this operation'
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/reports/generate',
|
||||
authenticateToken,
|
||||
expensiveLimiter,
|
||||
async (req, res) => {
|
||||
// Expensive operation
|
||||
}
|
||||
);
|
||||
\`\`\`
|
||||
|
||||
### Advanced: Per-User Rate Limiting
|
||||
|
||||
\`\`\`javascript
|
||||
// Different limits based on user tier
|
||||
function createTieredRateLimiter() {
|
||||
const limits = {
|
||||
free: { windowMs: 60 * 60 * 1000, max: 100 },
|
||||
pro: { windowMs: 60 * 60 * 1000, max: 1000 },
|
||||
enterprise: { windowMs: 60 * 60 * 1000, max: 10000 }
|
||||
};
|
||||
|
||||
return async (req, res, next) => {
|
||||
const user = req.user;
|
||||
const tier = user?.tier || 'free';
|
||||
const limit = limits[tier];
|
||||
|
||||
const key = \`rl:user:\${user.userId}\`;
|
||||
const current = await redis.incr(key);
|
||||
|
||||
if (current === 1) {
|
||||
await redis.expire(key, limit.windowMs / 1000);
|
||||
}
|
||||
|
||||
if (current > limit.max) {
|
||||
return res.status(429).json({
|
||||
error: 'Rate limit exceeded',
|
||||
limit: limit.max,
|
||||
remaining: 0,
|
||||
reset: await redis.ttl(key)
|
||||
});
|
||||
}
|
||||
|
||||
// Set rate limit headers
|
||||
res.set({
|
||||
'X-RateLimit-Limit': limit.max,
|
||||
'X-RateLimit-Remaining': limit.max - current,
|
||||
'X-RateLimit-Reset': await redis.ttl(key)
|
||||
});
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
app.use('/api/', authenticateToken, createTieredRateLimiter());
|
||||
\`\`\`
|
||||
|
||||
### DDoS Protection with Helmet
|
||||
|
||||
\`\`\`javascript
|
||||
const helmet = require('helmet');
|
||||
|
||||
app.use(helmet({
|
||||
// Content Security Policy
|
||||
contentSecurityPolicy: {
|
||||
directives: {
|
||||
defaultSrc: ["'self'"],
|
||||
styleSrc: ["'self'", "'unsafe-inline'"],
|
||||
scriptSrc: ["'self'"],
|
||||
imgSrc: ["'self'", 'data:', 'https:']
|
||||
}
|
||||
},
|
||||
// Prevent clickjacking
|
||||
frameguard: { action: 'deny' },
|
||||
// Hide X-Powered-By header
|
||||
hidePoweredBy: true,
|
||||
// Prevent MIME type sniffing
|
||||
noSniff: true,
|
||||
// Enable HSTS
|
||||
hsts: {
|
||||
maxAge: 31536000,
|
||||
includeSubDomains: true,
|
||||
preload: true
|
||||
}
|
||||
}));
|
||||
\`\`\`
|
||||
|
||||
### Rate Limit Response Headers
|
||||
|
||||
\`\`\`
|
||||
X-RateLimit-Limit: 100
|
||||
X-RateLimit-Remaining: 87
|
||||
X-RateLimit-Reset: 1640000000
|
||||
Retry-After: 900
|
||||
\`\`\`
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### ✅ Do This
|
||||
|
||||
- **Use HTTPS Everywhere** - Never send sensitive data over HTTP
|
||||
- **Implement Authentication** - Require authentication for protected endpoints
|
||||
- **Validate All Inputs** - Never trust user input
|
||||
- **Use Parameterized Queries** - Prevent SQL injection
|
||||
- **Implement Rate Limiting** - Protect against brute force and DDoS
|
||||
- **Hash Passwords** - Use bcrypt with salt rounds >= 10
|
||||
- **Use Short-Lived Tokens** - JWT access tokens should expire quickly
|
||||
- **Implement CORS Properly** - Only allow trusted origins
|
||||
- **Log Security Events** - Monitor for suspicious activity
|
||||
- **Keep Dependencies Updated** - Regularly update packages
|
||||
- **Use Security Headers** - Implement Helmet.js
|
||||
- **Sanitize Error Messages** - Don't leak sensitive information
|
||||
|
||||
### ❌ Don't Do This
|
||||
|
||||
- **Don't Store Passwords in Plain Text** - Always hash passwords
|
||||
- **Don't Use Weak Secrets** - Use strong, random JWT secrets
|
||||
- **Don't Trust User Input** - Always validate and sanitize
|
||||
- **Don't Expose Stack Traces** - Hide error details in production
|
||||
- **Don't Use String Concatenation for SQL** - Use parameterized queries
|
||||
- **Don't Store Sensitive Data in JWT** - JWTs are not encrypted
|
||||
- **Don't Ignore Security Updates** - Update dependencies regularly
|
||||
- **Don't Use Default Credentials** - Change all default passwords
|
||||
- **Don't Disable CORS Completely** - Configure it properly instead
|
||||
- **Don't Log Sensitive Data** - Sanitize logs
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
### Problem: JWT Secret Exposed in Code
|
||||
**Symptoms:** JWT secret hardcoded or committed to Git
|
||||
**Solution:**
|
||||
\`\`\`javascript
|
||||
// ❌ Bad
|
||||
const JWT_SECRET = 'my-secret-key';
|
||||
|
||||
// ✅ Good
|
||||
const JWT_SECRET = process.env.JWT_SECRET;
|
||||
if (!JWT_SECRET) {
|
||||
throw new Error('JWT_SECRET environment variable is required');
|
||||
}
|
||||
|
||||
// Generate strong secret
|
||||
// node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
|
||||
\`\`\`
|
||||
|
||||
### Problem: Weak Password Requirements
|
||||
**Symptoms:** Users can set weak passwords like "password123"
|
||||
**Solution:**
|
||||
\`\`\`javascript
|
||||
const passwordSchema = z.string()
|
||||
.min(12, 'Password must be at least 12 characters')
|
||||
.regex(/[A-Z]/, 'Must contain uppercase letter')
|
||||
.regex(/[a-z]/, 'Must contain lowercase letter')
|
||||
.regex(/[0-9]/, 'Must contain number')
|
||||
.regex(/[^A-Za-z0-9]/, 'Must contain special character');
|
||||
|
||||
// Or use a password strength library
|
||||
const zxcvbn = require('zxcvbn');
|
||||
const result = zxcvbn(password);
|
||||
if (result.score < 3) {
|
||||
return res.status(400).json({
|
||||
error: 'Password too weak',
|
||||
suggestions: result.feedback.suggestions
|
||||
});
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
### Problem: Missing Authorization Checks
|
||||
**Symptoms:** Users can access resources they shouldn't
|
||||
**Solution:**
|
||||
\`\`\`javascript
|
||||
// ❌ Bad: Only checks authentication
|
||||
app.delete('/api/posts/:id', authenticateToken, async (req, res) => {
|
||||
await prisma.post.delete({ where: { id: req.params.id } });
|
||||
res.json({ success: true });
|
||||
});
|
||||
|
||||
// ✅ Good: Checks both authentication and authorization
|
||||
app.delete('/api/posts/:id', authenticateToken, async (req, res) => {
|
||||
const post = await prisma.post.findUnique({
|
||||
where: { id: req.params.id }
|
||||
});
|
||||
|
||||
if (!post) {
|
||||
return res.status(404).json({ error: 'Post not found' });
|
||||
}
|
||||
|
||||
// Check if user owns the post or is admin
|
||||
if (post.userId !== req.user.userId && req.user.role !== 'admin') {
|
||||
return res.status(403).json({
|
||||
error: 'Not authorized to delete this post'
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.post.delete({ where: { id: req.params.id } });
|
||||
res.json({ success: true });
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
### Problem: Verbose Error Messages
|
||||
**Symptoms:** Error messages reveal system details
|
||||
**Solution:**
|
||||
\`\`\`javascript
|
||||
// ❌ Bad: Exposes database details
|
||||
app.post('/api/users', async (req, res) => {
|
||||
try {
|
||||
const user = await prisma.user.create({ data: req.body });
|
||||
res.json(user);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
// Error: "Unique constraint failed on the fields: (`email`)"
|
||||
}
|
||||
});
|
||||
|
||||
// ✅ Good: Generic error message
|
||||
app.post('/api/users', async (req, res) => {
|
||||
try {
|
||||
const user = await prisma.user.create({ data: req.body });
|
||||
res.json(user);
|
||||
} catch (error) {
|
||||
console.error('User creation error:', error); // Log full error
|
||||
|
||||
if (error.code === 'P2002') {
|
||||
return res.status(400).json({
|
||||
error: 'Email already exists'
|
||||
});
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
error: 'An error occurred while creating user'
|
||||
});
|
||||
}
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
## Security Checklist
|
||||
|
||||
### Authentication & Authorization
|
||||
- [ ] Implement strong authentication (JWT, OAuth 2.0)
|
||||
- [ ] Use HTTPS for all endpoints
|
||||
- [ ] Hash passwords with bcrypt (salt rounds >= 10)
|
||||
- [ ] Implement token expiration
|
||||
- [ ] Add refresh token mechanism
|
||||
- [ ] Verify user authorization for each request
|
||||
- [ ] Implement role-based access control (RBAC)
|
||||
|
||||
### Input Validation
|
||||
- [ ] Validate all user inputs
|
||||
- [ ] Use parameterized queries or ORM
|
||||
- [ ] Sanitize HTML content
|
||||
- [ ] Validate file uploads
|
||||
- [ ] Implement request schema validation
|
||||
- [ ] Use allowlists, not blocklists
|
||||
|
||||
### Rate Limiting & DDoS Protection
|
||||
- [ ] Implement rate limiting per user/IP
|
||||
- [ ] Add stricter limits for auth endpoints
|
||||
- [ ] Use Redis for distributed rate limiting
|
||||
- [ ] Return proper rate limit headers
|
||||
- [ ] Implement request throttling
|
||||
|
||||
### Data Protection
|
||||
- [ ] Use HTTPS/TLS for all traffic
|
||||
- [ ] Encrypt sensitive data at rest
|
||||
- [ ] Don't store sensitive data in JWT
|
||||
- [ ] Sanitize error messages
|
||||
- [ ] Implement proper CORS configuration
|
||||
- [ ] Use security headers (Helmet.js)
|
||||
|
||||
### Monitoring & Logging
|
||||
- [ ] Log security events
|
||||
- [ ] Monitor for suspicious activity
|
||||
- [ ] Set up alerts for failed auth attempts
|
||||
- [ ] Track API usage patterns
|
||||
- [ ] Don't log sensitive data
|
||||
|
||||
## OWASP API Security Top 10
|
||||
|
||||
1. **Broken Object Level Authorization** - Always verify user can access resource
|
||||
2. **Broken Authentication** - Implement strong authentication mechanisms
|
||||
3. **Broken Object Property Level Authorization** - Validate which properties user can access
|
||||
4. **Unrestricted Resource Consumption** - Implement rate limiting and quotas
|
||||
5. **Broken Function Level Authorization** - Verify user role for each function
|
||||
6. **Unrestricted Access to Sensitive Business Flows** - Protect critical workflows
|
||||
7. **Server Side Request Forgery (SSRF)** - Validate and sanitize URLs
|
||||
8. **Security Misconfiguration** - Use security best practices and headers
|
||||
9. **Improper Inventory Management** - Document and secure all API endpoints
|
||||
10. **Unsafe Consumption of APIs** - Validate data from third-party APIs
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `@ethical-hacking-methodology` - Security testing perspective
|
||||
- `@sql-injection-testing` - Testing for SQL injection
|
||||
- `@xss-html-injection` - Testing for XSS vulnerabilities
|
||||
- `@broken-authentication` - Authentication vulnerabilities
|
||||
- `@backend-dev-guidelines` - Backend development standards
|
||||
- `@systematic-debugging` - Debug security issues
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [OWASP API Security Top 10](https://owasp.org/www-project-api-security/)
|
||||
- [JWT Best Practices](https://tools.ietf.org/html/rfc8725)
|
||||
- [Express Security Best Practices](https://expressjs.com/en/advanced/best-practice-security.html)
|
||||
- [Node.js Security Checklist](https://blog.risingstack.com/node-js-security-checklist/)
|
||||
- [API Security Checklist](https://github.com/shieldfy/API-Security-Checklist)
|
||||
|
||||
---
|
||||
|
||||
**Pro Tip:** Security is not a one-time task - regularly audit your APIs, keep dependencies updated, and stay informed about new vulnerabilities!
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: auth-implementation-patterns
|
||||
description: "Build secure, scalable authentication and authorization systems using industry-standard patterns and modern best practices."
|
||||
risk: unknown
|
||||
source: community
|
||||
date_added: "2026-02-27"
|
||||
---
|
||||
|
||||
# Authentication & Authorization Implementation Patterns
|
||||
|
||||
Build secure, scalable authentication and authorization systems using industry-standard patterns and modern best practices.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Implementing user authentication systems
|
||||
- Securing REST or GraphQL APIs
|
||||
- Adding OAuth2/social login or SSO
|
||||
- Designing session management or RBAC
|
||||
- Debugging authentication or authorization issues
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- You only need UI copy or login page styling
|
||||
- The task is infrastructure-only without identity concerns
|
||||
- You cannot change auth policies or credential storage
|
||||
|
||||
## Instructions
|
||||
|
||||
- Define users, tenants, flows, and threat model constraints.
|
||||
- Choose auth strategy (session, JWT, OIDC) and token lifecycle.
|
||||
- Design authorization model and policy enforcement points.
|
||||
- Plan secrets storage, rotation, logging, and audit requirements.
|
||||
- If detailed examples are required, open `resources/implementation-playbook.md`.
|
||||
|
||||
## Safety
|
||||
|
||||
- Never log secrets, tokens, or credentials.
|
||||
- Enforce least privilege and secure storage for keys.
|
||||
|
||||
## Resources
|
||||
|
||||
- `resources/implementation-playbook.md` for detailed patterns and examples.
|
||||
@@ -0,0 +1,618 @@
|
||||
# Authentication and Authorization Implementation Patterns Implementation Playbook
|
||||
|
||||
This file contains detailed patterns, checklists, and code samples referenced by the skill.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### 1. Authentication vs Authorization
|
||||
|
||||
**Authentication (AuthN)**: Who are you?
|
||||
- Verifying identity (username/password, OAuth, biometrics)
|
||||
- Issuing credentials (sessions, tokens)
|
||||
- Managing login/logout
|
||||
|
||||
**Authorization (AuthZ)**: What can you do?
|
||||
- Permission checking
|
||||
- Role-based access control (RBAC)
|
||||
- Resource ownership validation
|
||||
- Policy enforcement
|
||||
|
||||
### 2. Authentication Strategies
|
||||
|
||||
**Session-Based:**
|
||||
- Server stores session state
|
||||
- Session ID in cookie
|
||||
- Traditional, simple, stateful
|
||||
|
||||
**Token-Based (JWT):**
|
||||
- Stateless, self-contained
|
||||
- Scales horizontally
|
||||
- Can store claims
|
||||
|
||||
**OAuth2/OpenID Connect:**
|
||||
- Delegate authentication
|
||||
- Social login (Google, GitHub)
|
||||
- Enterprise SSO
|
||||
|
||||
## JWT Authentication
|
||||
|
||||
### Pattern 1: JWT Implementation
|
||||
|
||||
```typescript
|
||||
// JWT structure: header.payload.signature
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
|
||||
interface JWTPayload {
|
||||
userId: string;
|
||||
email: string;
|
||||
role: string;
|
||||
iat: number;
|
||||
exp: number;
|
||||
}
|
||||
|
||||
// Generate JWT
|
||||
function generateTokens(userId: string, email: string, role: string) {
|
||||
const accessToken = jwt.sign(
|
||||
{ userId, email, role },
|
||||
process.env.JWT_SECRET!,
|
||||
{ expiresIn: '15m' } // Short-lived
|
||||
);
|
||||
|
||||
const refreshToken = jwt.sign(
|
||||
{ userId },
|
||||
process.env.JWT_REFRESH_SECRET!,
|
||||
{ expiresIn: '7d' } // Long-lived
|
||||
);
|
||||
|
||||
return { accessToken, refreshToken };
|
||||
}
|
||||
|
||||
// Verify JWT
|
||||
function verifyToken(token: string): JWTPayload {
|
||||
try {
|
||||
return jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload;
|
||||
} catch (error) {
|
||||
if (error instanceof jwt.TokenExpiredError) {
|
||||
throw new Error('Token expired');
|
||||
}
|
||||
if (error instanceof jwt.JsonWebTokenError) {
|
||||
throw new Error('Invalid token');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware
|
||||
function authenticate(req: Request, res: Response, next: NextFunction) {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader?.startsWith('Bearer ')) {
|
||||
return res.status(401).json({ error: 'No token provided' });
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7);
|
||||
try {
|
||||
const payload = verifyToken(token);
|
||||
req.user = payload; // Attach user to request
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(401).json({ error: 'Invalid token' });
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
app.get('/api/profile', authenticate, (req, res) => {
|
||||
res.json({ user: req.user });
|
||||
});
|
||||
```
|
||||
|
||||
### Pattern 2: Refresh Token Flow
|
||||
|
||||
```typescript
|
||||
interface StoredRefreshToken {
|
||||
token: string;
|
||||
userId: string;
|
||||
expiresAt: Date;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
class RefreshTokenService {
|
||||
// Store refresh token in database
|
||||
async storeRefreshToken(userId: string, refreshToken: string) {
|
||||
const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
|
||||
await db.refreshTokens.create({
|
||||
token: await hash(refreshToken), // Hash before storing
|
||||
userId,
|
||||
expiresAt,
|
||||
});
|
||||
}
|
||||
|
||||
// Refresh access token
|
||||
async refreshAccessToken(refreshToken: string) {
|
||||
// Verify refresh token
|
||||
let payload;
|
||||
try {
|
||||
payload = jwt.verify(
|
||||
refreshToken,
|
||||
process.env.JWT_REFRESH_SECRET!
|
||||
) as { userId: string };
|
||||
} catch {
|
||||
throw new Error('Invalid refresh token');
|
||||
}
|
||||
|
||||
// Check if token exists in database
|
||||
const storedToken = await db.refreshTokens.findOne({
|
||||
where: {
|
||||
token: await hash(refreshToken),
|
||||
userId: payload.userId,
|
||||
expiresAt: { $gt: new Date() },
|
||||
},
|
||||
});
|
||||
|
||||
if (!storedToken) {
|
||||
throw new Error('Refresh token not found or expired');
|
||||
}
|
||||
|
||||
// Get user
|
||||
const user = await db.users.findById(payload.userId);
|
||||
if (!user) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
|
||||
// Generate new access token
|
||||
const accessToken = jwt.sign(
|
||||
{ userId: user.id, email: user.email, role: user.role },
|
||||
process.env.JWT_SECRET!,
|
||||
{ expiresIn: '15m' }
|
||||
);
|
||||
|
||||
return { accessToken };
|
||||
}
|
||||
|
||||
// Revoke refresh token (logout)
|
||||
async revokeRefreshToken(refreshToken: string) {
|
||||
await db.refreshTokens.deleteOne({
|
||||
token: await hash(refreshToken),
|
||||
});
|
||||
}
|
||||
|
||||
// Revoke all user tokens (logout all devices)
|
||||
async revokeAllUserTokens(userId: string) {
|
||||
await db.refreshTokens.deleteMany({ userId });
|
||||
}
|
||||
}
|
||||
|
||||
// API endpoints
|
||||
app.post('/api/auth/refresh', async (req, res) => {
|
||||
const { refreshToken } = req.body;
|
||||
try {
|
||||
const { accessToken } = await refreshTokenService
|
||||
.refreshAccessToken(refreshToken);
|
||||
res.json({ accessToken });
|
||||
} catch (error) {
|
||||
res.status(401).json({ error: 'Invalid refresh token' });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/auth/logout', authenticate, async (req, res) => {
|
||||
const { refreshToken } = req.body;
|
||||
await refreshTokenService.revokeRefreshToken(refreshToken);
|
||||
res.json({ message: 'Logged out successfully' });
|
||||
});
|
||||
```
|
||||
|
||||
## Session-Based Authentication
|
||||
|
||||
### Pattern 1: Express Session
|
||||
|
||||
```typescript
|
||||
import session from 'express-session';
|
||||
import RedisStore from 'connect-redis';
|
||||
import { createClient } from 'redis';
|
||||
|
||||
// Setup Redis for session storage
|
||||
const redisClient = createClient({
|
||||
url: process.env.REDIS_URL,
|
||||
});
|
||||
await redisClient.connect();
|
||||
|
||||
app.use(
|
||||
session({
|
||||
store: new RedisStore({ client: redisClient }),
|
||||
secret: process.env.SESSION_SECRET!,
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
secure: process.env.NODE_ENV === 'production', // HTTPS only
|
||||
httpOnly: true, // No JavaScript access
|
||||
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
||||
sameSite: 'strict', // CSRF protection
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
// Login
|
||||
app.post('/api/auth/login', async (req, res) => {
|
||||
const { email, password } = req.body;
|
||||
|
||||
const user = await db.users.findOne({ email });
|
||||
if (!user || !(await verifyPassword(password, user.passwordHash))) {
|
||||
return res.status(401).json({ error: 'Invalid credentials' });
|
||||
}
|
||||
|
||||
// Store user in session
|
||||
req.session.userId = user.id;
|
||||
req.session.role = user.role;
|
||||
|
||||
res.json({ user: { id: user.id, email: user.email, role: user.role } });
|
||||
});
|
||||
|
||||
// Session middleware
|
||||
function requireAuth(req: Request, res: Response, next: NextFunction) {
|
||||
if (!req.session.userId) {
|
||||
return res.status(401).json({ error: 'Not authenticated' });
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
// Protected route
|
||||
app.get('/api/profile', requireAuth, async (req, res) => {
|
||||
const user = await db.users.findById(req.session.userId);
|
||||
res.json({ user });
|
||||
});
|
||||
|
||||
// Logout
|
||||
app.post('/api/auth/logout', (req, res) => {
|
||||
req.session.destroy((err) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ error: 'Logout failed' });
|
||||
}
|
||||
res.clearCookie('connect.sid');
|
||||
res.json({ message: 'Logged out successfully' });
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## OAuth2 / Social Login
|
||||
|
||||
### Pattern 1: OAuth2 with Passport.js
|
||||
|
||||
```typescript
|
||||
import passport from 'passport';
|
||||
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
|
||||
import { Strategy as GitHubStrategy } from 'passport-github2';
|
||||
|
||||
// Google OAuth
|
||||
passport.use(
|
||||
new GoogleStrategy(
|
||||
{
|
||||
clientID: process.env.GOOGLE_CLIENT_ID!,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
||||
callbackURL: '/api/auth/google/callback',
|
||||
},
|
||||
async (accessToken, refreshToken, profile, done) => {
|
||||
try {
|
||||
// Find or create user
|
||||
let user = await db.users.findOne({
|
||||
googleId: profile.id,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
user = await db.users.create({
|
||||
googleId: profile.id,
|
||||
email: profile.emails?.[0]?.value,
|
||||
name: profile.displayName,
|
||||
avatar: profile.photos?.[0]?.value,
|
||||
});
|
||||
}
|
||||
|
||||
return done(null, user);
|
||||
} catch (error) {
|
||||
return done(error, undefined);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Routes
|
||||
app.get('/api/auth/google', passport.authenticate('google', {
|
||||
scope: ['profile', 'email'],
|
||||
}));
|
||||
|
||||
app.get(
|
||||
'/api/auth/google/callback',
|
||||
passport.authenticate('google', { session: false }),
|
||||
(req, res) => {
|
||||
// Generate JWT
|
||||
const tokens = generateTokens(req.user.id, req.user.email, req.user.role);
|
||||
// Redirect to frontend with token
|
||||
res.redirect(`${process.env.FRONTEND_URL}/auth/callback?token=${tokens.accessToken}`);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## Authorization Patterns
|
||||
|
||||
### Pattern 1: Role-Based Access Control (RBAC)
|
||||
|
||||
```typescript
|
||||
enum Role {
|
||||
USER = 'user',
|
||||
MODERATOR = 'moderator',
|
||||
ADMIN = 'admin',
|
||||
}
|
||||
|
||||
const roleHierarchy: Record<Role, Role[]> = {
|
||||
[Role.ADMIN]: [Role.ADMIN, Role.MODERATOR, Role.USER],
|
||||
[Role.MODERATOR]: [Role.MODERATOR, Role.USER],
|
||||
[Role.USER]: [Role.USER],
|
||||
};
|
||||
|
||||
function hasRole(userRole: Role, requiredRole: Role): boolean {
|
||||
return roleHierarchy[userRole].includes(requiredRole);
|
||||
}
|
||||
|
||||
// Middleware
|
||||
function requireRole(...roles: Role[]) {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
if (!req.user) {
|
||||
return res.status(401).json({ error: 'Not authenticated' });
|
||||
}
|
||||
|
||||
if (!roles.some(role => hasRole(req.user.role, role))) {
|
||||
return res.status(403).json({ error: 'Insufficient permissions' });
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
// Usage
|
||||
app.delete('/api/users/:id',
|
||||
authenticate,
|
||||
requireRole(Role.ADMIN),
|
||||
async (req, res) => {
|
||||
// Only admins can delete users
|
||||
await db.users.delete(req.params.id);
|
||||
res.json({ message: 'User deleted' });
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Pattern 2: Permission-Based Access Control
|
||||
|
||||
```typescript
|
||||
enum Permission {
|
||||
READ_USERS = 'read:users',
|
||||
WRITE_USERS = 'write:users',
|
||||
DELETE_USERS = 'delete:users',
|
||||
READ_POSTS = 'read:posts',
|
||||
WRITE_POSTS = 'write:posts',
|
||||
}
|
||||
|
||||
const rolePermissions: Record<Role, Permission[]> = {
|
||||
[Role.USER]: [Permission.READ_POSTS, Permission.WRITE_POSTS],
|
||||
[Role.MODERATOR]: [
|
||||
Permission.READ_POSTS,
|
||||
Permission.WRITE_POSTS,
|
||||
Permission.READ_USERS,
|
||||
],
|
||||
[Role.ADMIN]: Object.values(Permission),
|
||||
};
|
||||
|
||||
function hasPermission(userRole: Role, permission: Permission): boolean {
|
||||
return rolePermissions[userRole]?.includes(permission) ?? false;
|
||||
}
|
||||
|
||||
function requirePermission(...permissions: Permission[]) {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
if (!req.user) {
|
||||
return res.status(401).json({ error: 'Not authenticated' });
|
||||
}
|
||||
|
||||
const hasAllPermissions = permissions.every(permission =>
|
||||
hasPermission(req.user.role, permission)
|
||||
);
|
||||
|
||||
if (!hasAllPermissions) {
|
||||
return res.status(403).json({ error: 'Insufficient permissions' });
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
// Usage
|
||||
app.get('/api/users',
|
||||
authenticate,
|
||||
requirePermission(Permission.READ_USERS),
|
||||
async (req, res) => {
|
||||
const users = await db.users.findAll();
|
||||
res.json({ users });
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### Pattern 3: Resource Ownership
|
||||
|
||||
```typescript
|
||||
// Check if user owns resource
|
||||
async function requireOwnership(
|
||||
resourceType: 'post' | 'comment',
|
||||
resourceIdParam: string = 'id'
|
||||
) {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
if (!req.user) {
|
||||
return res.status(401).json({ error: 'Not authenticated' });
|
||||
}
|
||||
|
||||
const resourceId = req.params[resourceIdParam];
|
||||
|
||||
// Admins can access anything
|
||||
if (req.user.role === Role.ADMIN) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// Check ownership
|
||||
let resource;
|
||||
if (resourceType === 'post') {
|
||||
resource = await db.posts.findById(resourceId);
|
||||
} else if (resourceType === 'comment') {
|
||||
resource = await db.comments.findById(resourceId);
|
||||
}
|
||||
|
||||
if (!resource) {
|
||||
return res.status(404).json({ error: 'Resource not found' });
|
||||
}
|
||||
|
||||
if (resource.userId !== req.user.userId) {
|
||||
return res.status(403).json({ error: 'Not authorized' });
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
// Usage
|
||||
app.put('/api/posts/:id',
|
||||
authenticate,
|
||||
requireOwnership('post'),
|
||||
async (req, res) => {
|
||||
// User can only update their own posts
|
||||
const post = await db.posts.update(req.params.id, req.body);
|
||||
res.json({ post });
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### Pattern 1: Password Security
|
||||
|
||||
```typescript
|
||||
import bcrypt from 'bcrypt';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Password validation schema
|
||||
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');
|
||||
|
||||
// Hash password
|
||||
async function hashPassword(password: string): Promise<string> {
|
||||
const saltRounds = 12; // 2^12 iterations
|
||||
return bcrypt.hash(password, saltRounds);
|
||||
}
|
||||
|
||||
// Verify password
|
||||
async function verifyPassword(
|
||||
password: string,
|
||||
hash: string
|
||||
): Promise<boolean> {
|
||||
return bcrypt.compare(password, hash);
|
||||
}
|
||||
|
||||
// Registration with password validation
|
||||
app.post('/api/auth/register', async (req, res) => {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
|
||||
// Validate password
|
||||
passwordSchema.parse(password);
|
||||
|
||||
// Check if user exists
|
||||
const existingUser = await db.users.findOne({ email });
|
||||
if (existingUser) {
|
||||
return res.status(400).json({ error: 'Email already registered' });
|
||||
}
|
||||
|
||||
// Hash password
|
||||
const passwordHash = await hashPassword(password);
|
||||
|
||||
// Create user
|
||||
const user = await db.users.create({
|
||||
email,
|
||||
passwordHash,
|
||||
});
|
||||
|
||||
// Generate tokens
|
||||
const tokens = generateTokens(user.id, user.email, user.role);
|
||||
|
||||
res.status(201).json({
|
||||
user: { id: user.id, email: user.email },
|
||||
...tokens,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
return res.status(400).json({ error: error.errors[0].message });
|
||||
}
|
||||
res.status(500).json({ error: 'Registration failed' });
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Pattern 2: Rate Limiting
|
||||
|
||||
```typescript
|
||||
import rateLimit from 'express-rate-limit';
|
||||
import RedisStore from 'rate-limit-redis';
|
||||
|
||||
// Login rate limiter
|
||||
const loginLimiter = rateLimit({
|
||||
store: new RedisStore({ client: redisClient }),
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 5, // 5 attempts
|
||||
message: 'Too many login attempts, please try again later',
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
});
|
||||
|
||||
// API rate limiter
|
||||
const apiLimiter = rateLimit({
|
||||
windowMs: 60 * 1000, // 1 minute
|
||||
max: 100, // 100 requests per minute
|
||||
standardHeaders: true,
|
||||
});
|
||||
|
||||
// Apply to routes
|
||||
app.post('/api/auth/login', loginLimiter, async (req, res) => {
|
||||
// Login logic
|
||||
});
|
||||
|
||||
app.use('/api/', apiLimiter);
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Never Store Plain Passwords**: Always hash with bcrypt/argon2
|
||||
2. **Use HTTPS**: Encrypt data in transit
|
||||
3. **Short-Lived Access Tokens**: 15-30 minutes max
|
||||
4. **Secure Cookies**: httpOnly, secure, sameSite flags
|
||||
5. **Validate All Input**: Email format, password strength
|
||||
6. **Rate Limit Auth Endpoints**: Prevent brute force attacks
|
||||
7. **Implement CSRF Protection**: For session-based auth
|
||||
8. **Rotate Secrets Regularly**: JWT secrets, session secrets
|
||||
9. **Log Security Events**: Login attempts, failed auth
|
||||
10. **Use MFA When Possible**: Extra security layer
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- **Weak Passwords**: Enforce strong password policies
|
||||
- **JWT in localStorage**: Vulnerable to XSS, use httpOnly cookies
|
||||
- **No Token Expiration**: Tokens should expire
|
||||
- **Client-Side Auth Checks Only**: Always validate server-side
|
||||
- **Insecure Password Reset**: Use secure tokens with expiration
|
||||
- **No Rate Limiting**: Vulnerable to brute force
|
||||
- **Trusting Client Data**: Always validate on server
|
||||
|
||||
## Resources
|
||||
|
||||
- **references/jwt-best-practices.md**: JWT implementation guide
|
||||
- **references/oauth2-flows.md**: OAuth2 flow diagrams and examples
|
||||
- **references/session-security.md**: Secure session management
|
||||
- **assets/auth-security-checklist.md**: Security review checklist
|
||||
- **assets/password-policy-template.md**: Password requirements template
|
||||
- **scripts/token-validator.ts**: JWT validation utility
|
||||
@@ -0,0 +1,155 @@
|
||||
---
|
||||
name: backend-security-coder
|
||||
description: Expert in secure backend coding practices specializing in input validation, authentication, and API security. Use PROACTIVELY for backend security implementations or security code reviews.
|
||||
risk: unknown
|
||||
source: community
|
||||
date_added: '2026-02-27'
|
||||
---
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Working on backend security coder tasks or workflows
|
||||
- Needing guidance, best practices, or checklists for backend security coder
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- The task is unrelated to backend security coder
|
||||
- You need a different domain or tool outside this scope
|
||||
|
||||
## Instructions
|
||||
|
||||
- Clarify goals, constraints, and required inputs.
|
||||
- Apply relevant best practices and validate outcomes.
|
||||
- Provide actionable steps and verification.
|
||||
- If detailed examples are required, open `resources/implementation-playbook.md`.
|
||||
|
||||
You are a backend security coding expert specializing in secure development practices, vulnerability prevention, and secure architecture implementation.
|
||||
|
||||
## Purpose
|
||||
Expert backend security developer with comprehensive knowledge of secure coding practices, vulnerability prevention, and defensive programming techniques. Masters input validation, authentication systems, API security, database protection, and secure error handling. Specializes in building security-first backend applications that resist common attack vectors.
|
||||
|
||||
## When to Use vs Security Auditor
|
||||
- **Use this agent for**: Hands-on backend security coding, API security implementation, database security configuration, authentication system coding, vulnerability fixes
|
||||
- **Use security-auditor for**: High-level security audits, compliance assessments, DevSecOps pipeline design, threat modeling, security architecture reviews, penetration testing planning
|
||||
- **Key difference**: This agent focuses on writing secure backend code, while security-auditor focuses on auditing and assessing security posture
|
||||
|
||||
## Capabilities
|
||||
|
||||
### General Secure Coding Practices
|
||||
- **Input validation and sanitization**: Comprehensive input validation frameworks, allowlist approaches, data type enforcement
|
||||
- **Injection attack prevention**: SQL injection, NoSQL injection, LDAP injection, command injection prevention techniques
|
||||
- **Error handling security**: Secure error messages, logging without information leakage, graceful degradation
|
||||
- **Sensitive data protection**: Data classification, secure storage patterns, encryption at rest and in transit
|
||||
- **Secret management**: Secure credential storage, environment variable best practices, secret rotation strategies
|
||||
- **Output encoding**: Context-aware encoding, preventing injection in templates and APIs
|
||||
|
||||
### HTTP Security Headers and Cookies
|
||||
- **Content Security Policy (CSP)**: CSP implementation, nonce and hash strategies, report-only mode
|
||||
- **Security headers**: HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy implementation
|
||||
- **Cookie security**: HttpOnly, Secure, SameSite attributes, cookie scoping and domain restrictions
|
||||
- **CORS configuration**: Strict CORS policies, preflight request handling, credential-aware CORS
|
||||
- **Session management**: Secure session handling, session fixation prevention, timeout management
|
||||
|
||||
### CSRF Protection
|
||||
- **Anti-CSRF tokens**: Token generation, validation, and refresh strategies for cookie-based authentication
|
||||
- **Header validation**: Origin and Referer header validation for non-GET requests
|
||||
- **Double-submit cookies**: CSRF token implementation in cookies and headers
|
||||
- **SameSite cookie enforcement**: Leveraging SameSite attributes for CSRF protection
|
||||
- **State-changing operation protection**: Authentication requirements for sensitive actions
|
||||
|
||||
### Output Rendering Security
|
||||
- **Context-aware encoding**: HTML, JavaScript, CSS, URL encoding based on output context
|
||||
- **Template security**: Secure templating practices, auto-escaping configuration
|
||||
- **JSON response security**: Preventing JSON hijacking, secure API response formatting
|
||||
- **XML security**: XML external entity (XXE) prevention, secure XML parsing
|
||||
- **File serving security**: Secure file download, content-type validation, path traversal prevention
|
||||
|
||||
### Database Security
|
||||
- **Parameterized queries**: Prepared statements, ORM security configuration, query parameterization
|
||||
- **Database authentication**: Connection security, credential management, connection pooling security
|
||||
- **Data encryption**: Field-level encryption, transparent data encryption, key management
|
||||
- **Access control**: Database user privilege separation, role-based access control
|
||||
- **Audit logging**: Database activity monitoring, change tracking, compliance logging
|
||||
- **Backup security**: Secure backup procedures, encryption of backups, access control for backup files
|
||||
|
||||
### API Security
|
||||
- **Authentication mechanisms**: JWT security, OAuth 2.0/2.1 implementation, API key management
|
||||
- **Authorization patterns**: RBAC, ABAC, scope-based access control, fine-grained permissions
|
||||
- **Input validation**: API request validation, payload size limits, content-type validation
|
||||
- **Rate limiting**: Request throttling, burst protection, user-based and IP-based limiting
|
||||
- **API versioning security**: Secure version management, backward compatibility security
|
||||
- **Error handling**: Consistent error responses, security-aware error messages, logging strategies
|
||||
|
||||
### External Requests Security
|
||||
- **Allowlist management**: Destination allowlisting, URL validation, domain restriction
|
||||
- **Request validation**: URL sanitization, protocol restrictions, parameter validation
|
||||
- **SSRF prevention**: Server-side request forgery protection, internal network isolation
|
||||
- **Timeout and limits**: Request timeout configuration, response size limits, resource protection
|
||||
- **Certificate validation**: SSL/TLS certificate pinning, certificate authority validation
|
||||
- **Proxy security**: Secure proxy configuration, header forwarding restrictions
|
||||
|
||||
### Authentication and Authorization
|
||||
- **Multi-factor authentication**: TOTP, hardware tokens, biometric integration, backup codes
|
||||
- **Password security**: Hashing algorithms (bcrypt, Argon2), salt generation, password policies
|
||||
- **Session security**: Secure session tokens, session invalidation, concurrent session management
|
||||
- **JWT implementation**: Secure JWT handling, signature verification, token expiration
|
||||
- **OAuth security**: Secure OAuth flows, PKCE implementation, scope validation
|
||||
|
||||
### Logging and Monitoring
|
||||
- **Security logging**: Authentication events, authorization failures, suspicious activity tracking
|
||||
- **Log sanitization**: Preventing log injection, sensitive data exclusion from logs
|
||||
- **Audit trails**: Comprehensive activity logging, tamper-evident logging, log integrity
|
||||
- **Monitoring integration**: SIEM integration, alerting on security events, anomaly detection
|
||||
- **Compliance logging**: Regulatory requirement compliance, retention policies, log encryption
|
||||
|
||||
### Cloud and Infrastructure Security
|
||||
- **Environment configuration**: Secure environment variable management, configuration encryption
|
||||
- **Container security**: Secure Docker practices, image scanning, runtime security
|
||||
- **Secrets management**: Integration with HashiCorp Vault, AWS Secrets Manager, Azure Key Vault
|
||||
- **Network security**: VPC configuration, security groups, network segmentation
|
||||
- **Identity and access management**: IAM roles, service account security, principle of least privilege
|
||||
|
||||
## Behavioral Traits
|
||||
- Validates and sanitizes all user inputs using allowlist approaches
|
||||
- Implements defense-in-depth with multiple security layers
|
||||
- Uses parameterized queries and prepared statements exclusively
|
||||
- Never exposes sensitive information in error messages or logs
|
||||
- Applies principle of least privilege to all access controls
|
||||
- Implements comprehensive audit logging for security events
|
||||
- Uses secure defaults and fails securely in error conditions
|
||||
- Regularly updates dependencies and monitors for vulnerabilities
|
||||
- Considers security implications in every design decision
|
||||
- Maintains separation of concerns between security layers
|
||||
|
||||
## Knowledge Base
|
||||
- OWASP Top 10 and secure coding guidelines
|
||||
- Common vulnerability patterns and prevention techniques
|
||||
- Authentication and authorization best practices
|
||||
- Database security and query parameterization
|
||||
- HTTP security headers and cookie security
|
||||
- Input validation and output encoding techniques
|
||||
- Secure error handling and logging practices
|
||||
- API security and rate limiting strategies
|
||||
- CSRF and SSRF prevention mechanisms
|
||||
- Secret management and encryption practices
|
||||
|
||||
## Response Approach
|
||||
1. **Assess security requirements** including threat model and compliance needs
|
||||
2. **Implement input validation** with comprehensive sanitization and allowlist approaches
|
||||
3. **Configure secure authentication** with multi-factor authentication and session management
|
||||
4. **Apply database security** with parameterized queries and access controls
|
||||
5. **Set security headers** and implement CSRF protection for web applications
|
||||
6. **Implement secure API design** with proper authentication and rate limiting
|
||||
7. **Configure secure external requests** with allowlists and validation
|
||||
8. **Set up security logging** and monitoring for threat detection
|
||||
9. **Review and test security controls** with both automated and manual testing
|
||||
|
||||
## Example Interactions
|
||||
- "Implement secure user authentication with JWT and refresh token rotation"
|
||||
- "Review this API endpoint for injection vulnerabilities and implement proper validation"
|
||||
- "Configure CSRF protection for cookie-based authentication system"
|
||||
- "Implement secure database queries with parameterization and access controls"
|
||||
- "Set up comprehensive security headers and CSP for web application"
|
||||
- "Create secure error handling that doesn't leak sensitive information"
|
||||
- "Implement rate limiting and DDoS protection for public API endpoints"
|
||||
- "Design secure external service integration with allowlist validation"
|
||||
@@ -0,0 +1,499 @@
|
||||
---
|
||||
name: cc-skill-security-review
|
||||
description: "This skill ensures all code follows security best practices and identifies potential vulnerabilities. Use when implementing authentication or authorization, handling user input or file uploads, or creating new API endpoints."
|
||||
risk: unknown
|
||||
source: community
|
||||
date_added: "2026-02-27"
|
||||
---
|
||||
|
||||
# Security Review Skill
|
||||
|
||||
This skill ensures all code follows security best practices and identifies potential vulnerabilities.
|
||||
|
||||
## When to Use
|
||||
- Implementing authentication or authorization
|
||||
- Handling user input or file uploads
|
||||
- Creating new API endpoints
|
||||
- Working with secrets or credentials
|
||||
- Implementing payment features
|
||||
- Storing or transmitting sensitive data
|
||||
- Integrating third-party APIs
|
||||
|
||||
## Security Checklist
|
||||
|
||||
### 1. Secrets Management
|
||||
|
||||
#### ❌ NEVER Do This
|
||||
```typescript
|
||||
const apiKey = "sk-proj-xxxxx" // Hardcoded secret
|
||||
const dbPassword = "password123" // In source code
|
||||
```
|
||||
|
||||
#### ✅ ALWAYS Do This
|
||||
```typescript
|
||||
const apiKey = process.env.OPENAI_API_KEY
|
||||
const dbUrl = process.env.DATABASE_URL
|
||||
|
||||
// Verify secrets exist
|
||||
if (!apiKey) {
|
||||
throw new Error('OPENAI_API_KEY not configured')
|
||||
}
|
||||
```
|
||||
|
||||
#### Verification Steps
|
||||
- [ ] No hardcoded API keys, tokens, or passwords
|
||||
- [ ] All secrets in environment variables
|
||||
- [ ] `.env.local` in .gitignore
|
||||
- [ ] No secrets in git history
|
||||
- [ ] Production secrets in hosting platform (Vercel, Railway)
|
||||
|
||||
### 2. Input Validation
|
||||
|
||||
#### Always Validate User Input
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
|
||||
// Define validation schema
|
||||
const CreateUserSchema = z.object({
|
||||
email: z.string().email(),
|
||||
name: z.string().min(1).max(100),
|
||||
age: z.number().int().min(0).max(150)
|
||||
})
|
||||
|
||||
// Validate before processing
|
||||
export async function createUser(input: unknown) {
|
||||
try {
|
||||
const validated = CreateUserSchema.parse(input)
|
||||
return await db.users.create(validated)
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
return { success: false, errors: error.errors }
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### File Upload Validation
|
||||
```typescript
|
||||
function validateFileUpload(file: File) {
|
||||
// Size check (5MB max)
|
||||
const maxSize = 5 * 1024 * 1024
|
||||
if (file.size > maxSize) {
|
||||
throw new Error('File too large (max 5MB)')
|
||||
}
|
||||
|
||||
// Type check
|
||||
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']
|
||||
if (!allowedTypes.includes(file.type)) {
|
||||
throw new Error('Invalid file type')
|
||||
}
|
||||
|
||||
// Extension check
|
||||
const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif']
|
||||
const extension = file.name.toLowerCase().match(/\.[^.]+$/)?.[0]
|
||||
if (!extension || !allowedExtensions.includes(extension)) {
|
||||
throw new Error('Invalid file extension')
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
#### Verification Steps
|
||||
- [ ] All user inputs validated with schemas
|
||||
- [ ] File uploads restricted (size, type, extension)
|
||||
- [ ] No direct use of user input in queries
|
||||
- [ ] Whitelist validation (not blacklist)
|
||||
- [ ] Error messages don't leak sensitive info
|
||||
|
||||
### 3. SQL Injection Prevention
|
||||
|
||||
#### ❌ NEVER Concatenate SQL
|
||||
```typescript
|
||||
// DANGEROUS - SQL Injection vulnerability
|
||||
const query = `SELECT * FROM users WHERE email = '${userEmail}'`
|
||||
await db.query(query)
|
||||
```
|
||||
|
||||
#### ✅ ALWAYS Use Parameterized Queries
|
||||
```typescript
|
||||
// Safe - parameterized query
|
||||
const { data } = await supabase
|
||||
.from('users')
|
||||
.select('*')
|
||||
.eq('email', userEmail)
|
||||
|
||||
// Or with raw SQL
|
||||
await db.query(
|
||||
'SELECT * FROM users WHERE email = $1',
|
||||
[userEmail]
|
||||
)
|
||||
```
|
||||
|
||||
#### Verification Steps
|
||||
- [ ] All database queries use parameterized queries
|
||||
- [ ] No string concatenation in SQL
|
||||
- [ ] ORM/query builder used correctly
|
||||
- [ ] Supabase queries properly sanitized
|
||||
|
||||
### 4. Authentication & Authorization
|
||||
|
||||
#### JWT Token Handling
|
||||
```typescript
|
||||
// ❌ WRONG: localStorage (vulnerable to XSS)
|
||||
localStorage.setItem('token', token)
|
||||
|
||||
// ✅ CORRECT: httpOnly cookies
|
||||
res.setHeader('Set-Cookie',
|
||||
`token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
|
||||
```
|
||||
|
||||
#### Authorization Checks
|
||||
```typescript
|
||||
export async function deleteUser(userId: string, requesterId: string) {
|
||||
// ALWAYS verify authorization first
|
||||
const requester = await db.users.findUnique({
|
||||
where: { id: requesterId }
|
||||
})
|
||||
|
||||
if (requester.role !== 'admin') {
|
||||
return NextResponse.json(
|
||||
{ error: 'Unauthorized' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
// Proceed with deletion
|
||||
await db.users.delete({ where: { id: userId } })
|
||||
}
|
||||
```
|
||||
|
||||
#### Row Level Security (Supabase)
|
||||
```sql
|
||||
-- Enable RLS on all tables
|
||||
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Users can only view their own data
|
||||
CREATE POLICY "Users view own data"
|
||||
ON users FOR SELECT
|
||||
USING (auth.uid() = id);
|
||||
|
||||
-- Users can only update their own data
|
||||
CREATE POLICY "Users update own data"
|
||||
ON users FOR UPDATE
|
||||
USING (auth.uid() = id);
|
||||
```
|
||||
|
||||
#### Verification Steps
|
||||
- [ ] Tokens stored in httpOnly cookies (not localStorage)
|
||||
- [ ] Authorization checks before sensitive operations
|
||||
- [ ] Row Level Security enabled in Supabase
|
||||
- [ ] Role-based access control implemented
|
||||
- [ ] Session management secure
|
||||
|
||||
### 5. XSS Prevention
|
||||
|
||||
#### Sanitize HTML
|
||||
```typescript
|
||||
import DOMPurify from 'isomorphic-dompurify'
|
||||
|
||||
// ALWAYS sanitize user-provided HTML
|
||||
function renderUserContent(html: string) {
|
||||
const clean = DOMPurify.sanitize(html, {
|
||||
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p'],
|
||||
ALLOWED_ATTR: []
|
||||
})
|
||||
return <div dangerouslySetInnerHTML={{ __html: clean }} />
|
||||
}
|
||||
```
|
||||
|
||||
#### Content Security Policy
|
||||
```typescript
|
||||
// next.config.js
|
||||
const securityHeaders = [
|
||||
{
|
||||
key: 'Content-Security-Policy',
|
||||
value: `
|
||||
default-src 'self';
|
||||
script-src 'self' 'unsafe-eval' 'unsafe-inline';
|
||||
style-src 'self' 'unsafe-inline';
|
||||
img-src 'self' data: https:;
|
||||
font-src 'self';
|
||||
connect-src 'self' https://api.example.com;
|
||||
`.replace(/\s{2,}/g, ' ').trim()
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### Verification Steps
|
||||
- [ ] User-provided HTML sanitized
|
||||
- [ ] CSP headers configured
|
||||
- [ ] No unvalidated dynamic content rendering
|
||||
- [ ] React's built-in XSS protection used
|
||||
|
||||
### 6. CSRF Protection
|
||||
|
||||
#### CSRF Tokens
|
||||
```typescript
|
||||
import { csrf } from '@/lib/csrf'
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const token = request.headers.get('X-CSRF-Token')
|
||||
|
||||
if (!csrf.verify(token)) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Invalid CSRF token' },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
|
||||
// Process request
|
||||
}
|
||||
```
|
||||
|
||||
#### SameSite Cookies
|
||||
```typescript
|
||||
res.setHeader('Set-Cookie',
|
||||
`session=${sessionId}; HttpOnly; Secure; SameSite=Strict`)
|
||||
```
|
||||
|
||||
#### Verification Steps
|
||||
- [ ] CSRF tokens on state-changing operations
|
||||
- [ ] SameSite=Strict on all cookies
|
||||
- [ ] Double-submit cookie pattern implemented
|
||||
|
||||
### 7. Rate Limiting
|
||||
|
||||
#### API Rate Limiting
|
||||
```typescript
|
||||
import rateLimit from 'express-rate-limit'
|
||||
|
||||
const limiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 100, // 100 requests per window
|
||||
message: 'Too many requests'
|
||||
})
|
||||
|
||||
// Apply to routes
|
||||
app.use('/api/', limiter)
|
||||
```
|
||||
|
||||
#### Expensive Operations
|
||||
```typescript
|
||||
// Aggressive rate limiting for searches
|
||||
const searchLimiter = rateLimit({
|
||||
windowMs: 60 * 1000, // 1 minute
|
||||
max: 10, // 10 requests per minute
|
||||
message: 'Too many search requests'
|
||||
})
|
||||
|
||||
app.use('/api/search', searchLimiter)
|
||||
```
|
||||
|
||||
#### Verification Steps
|
||||
- [ ] Rate limiting on all API endpoints
|
||||
- [ ] Stricter limits on expensive operations
|
||||
- [ ] IP-based rate limiting
|
||||
- [ ] User-based rate limiting (authenticated)
|
||||
|
||||
### 8. Sensitive Data Exposure
|
||||
|
||||
#### Logging
|
||||
```typescript
|
||||
// ❌ WRONG: Logging sensitive data
|
||||
console.log('User login:', { email, password })
|
||||
console.log('Payment:', { cardNumber, cvv })
|
||||
|
||||
// ✅ CORRECT: Redact sensitive data
|
||||
console.log('User login:', { email, userId })
|
||||
console.log('Payment:', { last4: card.last4, userId })
|
||||
```
|
||||
|
||||
#### Error Messages
|
||||
```typescript
|
||||
// ❌ WRONG: Exposing internal details
|
||||
catch (error) {
|
||||
return NextResponse.json(
|
||||
{ error: error.message, stack: error.stack },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
// ✅ CORRECT: Generic error messages
|
||||
catch (error) {
|
||||
console.error('Internal error:', error)
|
||||
return NextResponse.json(
|
||||
{ error: 'An error occurred. Please try again.' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### Verification Steps
|
||||
- [ ] No passwords, tokens, or secrets in logs
|
||||
- [ ] Error messages generic for users
|
||||
- [ ] Detailed errors only in server logs
|
||||
- [ ] No stack traces exposed to users
|
||||
|
||||
### 9. Blockchain Security (Solana)
|
||||
|
||||
#### Wallet Verification
|
||||
```typescript
|
||||
import { verify } from '@solana/web3.js'
|
||||
|
||||
async function verifyWalletOwnership(
|
||||
publicKey: string,
|
||||
signature: string,
|
||||
message: string
|
||||
) {
|
||||
try {
|
||||
const isValid = verify(
|
||||
Buffer.from(message),
|
||||
Buffer.from(signature, 'base64'),
|
||||
Buffer.from(publicKey, 'base64')
|
||||
)
|
||||
return isValid
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Transaction Verification
|
||||
```typescript
|
||||
async function verifyTransaction(transaction: Transaction) {
|
||||
// Verify recipient
|
||||
if (transaction.to !== expectedRecipient) {
|
||||
throw new Error('Invalid recipient')
|
||||
}
|
||||
|
||||
// Verify amount
|
||||
if (transaction.amount > maxAmount) {
|
||||
throw new Error('Amount exceeds limit')
|
||||
}
|
||||
|
||||
// Verify user has sufficient balance
|
||||
const balance = await getBalance(transaction.from)
|
||||
if (balance < transaction.amount) {
|
||||
throw new Error('Insufficient balance')
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
#### Verification Steps
|
||||
- [ ] Wallet signatures verified
|
||||
- [ ] Transaction details validated
|
||||
- [ ] Balance checks before transactions
|
||||
- [ ] No blind transaction signing
|
||||
|
||||
### 10. Dependency Security
|
||||
|
||||
#### Regular Updates
|
||||
```bash
|
||||
# Check for vulnerabilities
|
||||
npm audit
|
||||
|
||||
# Fix automatically fixable issues
|
||||
npm audit fix
|
||||
|
||||
# Update dependencies
|
||||
npm update
|
||||
|
||||
# Check for outdated packages
|
||||
npm outdated
|
||||
```
|
||||
|
||||
#### Lock Files
|
||||
```bash
|
||||
# ALWAYS commit lock files
|
||||
git add package-lock.json
|
||||
|
||||
# Use in CI/CD for reproducible builds
|
||||
npm ci # Instead of npm install
|
||||
```
|
||||
|
||||
#### Verification Steps
|
||||
- [ ] Dependencies up to date
|
||||
- [ ] No known vulnerabilities (npm audit clean)
|
||||
- [ ] Lock files committed
|
||||
- [ ] Dependabot enabled on GitHub
|
||||
- [ ] Regular security updates
|
||||
|
||||
## Security Testing
|
||||
|
||||
### Automated Security Tests
|
||||
```typescript
|
||||
// Test authentication
|
||||
test('requires authentication', async () => {
|
||||
const response = await fetch('/api/protected')
|
||||
expect(response.status).toBe(401)
|
||||
})
|
||||
|
||||
// Test authorization
|
||||
test('requires admin role', async () => {
|
||||
const response = await fetch('/api/admin', {
|
||||
headers: { Authorization: `Bearer ${userToken}` }
|
||||
})
|
||||
expect(response.status).toBe(403)
|
||||
})
|
||||
|
||||
// Test input validation
|
||||
test('rejects invalid input', async () => {
|
||||
const response = await fetch('/api/users', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ email: 'not-an-email' })
|
||||
})
|
||||
expect(response.status).toBe(400)
|
||||
})
|
||||
|
||||
// Test rate limiting
|
||||
test('enforces rate limits', async () => {
|
||||
const requests = Array(101).fill(null).map(() =>
|
||||
fetch('/api/endpoint')
|
||||
)
|
||||
|
||||
const responses = await Promise.all(requests)
|
||||
const tooManyRequests = responses.filter(r => r.status === 429)
|
||||
|
||||
expect(tooManyRequests.length).toBeGreaterThan(0)
|
||||
})
|
||||
```
|
||||
|
||||
## Pre-Deployment Security Checklist
|
||||
|
||||
Before ANY production deployment:
|
||||
|
||||
- [ ] **Secrets**: No hardcoded secrets, all in env vars
|
||||
- [ ] **Input Validation**: All user inputs validated
|
||||
- [ ] **SQL Injection**: All queries parameterized
|
||||
- [ ] **XSS**: User content sanitized
|
||||
- [ ] **CSRF**: Protection enabled
|
||||
- [ ] **Authentication**: Proper token handling
|
||||
- [ ] **Authorization**: Role checks in place
|
||||
- [ ] **Rate Limiting**: Enabled on all endpoints
|
||||
- [ ] **HTTPS**: Enforced in production
|
||||
- [ ] **Security Headers**: CSP, X-Frame-Options configured
|
||||
- [ ] **Error Handling**: No sensitive data in errors
|
||||
- [ ] **Logging**: No sensitive data logged
|
||||
- [ ] **Dependencies**: Up to date, no vulnerabilities
|
||||
- [ ] **Row Level Security**: Enabled in Supabase
|
||||
- [ ] **CORS**: Properly configured
|
||||
- [ ] **File Uploads**: Validated (size, type)
|
||||
- [ ] **Wallet Signatures**: Verified (if blockchain)
|
||||
|
||||
## Resources
|
||||
|
||||
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
|
||||
- [Next.js Security](https://nextjs.org/docs/security)
|
||||
- [Supabase Security](https://supabase.com/docs/guides/auth)
|
||||
- [Web Security Academy](https://portswigger.net/web-security)
|
||||
|
||||
---
|
||||
|
||||
**Remember**: Security is not optional. One vulnerability can compromise the entire platform. When in doubt, err on the side of caution.
|
||||
|
||||
## When to Use
|
||||
This skill is applicable to execute the workflow or actions described in the overview.
|
||||
@@ -0,0 +1,168 @@
|
||||
---
|
||||
name: frontend-security-coder
|
||||
description: Expert in secure frontend coding practices specializing in XSS prevention, output sanitization, and client-side security patterns.
|
||||
risk: unknown
|
||||
source: community
|
||||
date_added: '2026-02-27'
|
||||
---
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Working on frontend security coder tasks or workflows
|
||||
- Needing guidance, best practices, or checklists for frontend security coder
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- The task is unrelated to frontend security coder
|
||||
- You need a different domain or tool outside this scope
|
||||
|
||||
## Instructions
|
||||
|
||||
- Clarify goals, constraints, and required inputs.
|
||||
- Apply relevant best practices and validate outcomes.
|
||||
- Provide actionable steps and verification.
|
||||
- If detailed examples are required, open `resources/implementation-playbook.md`.
|
||||
|
||||
You are a frontend security coding expert specializing in client-side security practices, XSS prevention, and secure user interface development.
|
||||
|
||||
## Purpose
|
||||
Expert frontend security developer with comprehensive knowledge of client-side security practices, DOM security, and browser-based vulnerability prevention. Masters XSS prevention, safe DOM manipulation, Content Security Policy implementation, and secure user interaction patterns. Specializes in building security-first frontend applications that protect users from client-side attacks.
|
||||
|
||||
## When to Use vs Security Auditor
|
||||
- **Use this agent for**: Hands-on frontend security coding, XSS prevention implementation, CSP configuration, secure DOM manipulation, client-side vulnerability fixes
|
||||
- **Use security-auditor for**: High-level security audits, compliance assessments, DevSecOps pipeline design, threat modeling, security architecture reviews, penetration testing planning
|
||||
- **Key difference**: This agent focuses on writing secure frontend code, while security-auditor focuses on auditing and assessing security posture
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Output Handling and XSS Prevention
|
||||
- **Safe DOM manipulation**: textContent vs innerHTML security, secure element creation and modification
|
||||
- **Dynamic content sanitization**: DOMPurify integration, HTML sanitization libraries, custom sanitization rules
|
||||
- **Context-aware encoding**: HTML entity encoding, JavaScript string escaping, URL encoding
|
||||
- **Template security**: Secure templating practices, auto-escaping configuration, template injection prevention
|
||||
- **User-generated content**: Safe rendering of user inputs, markdown sanitization, rich text editor security
|
||||
- **Document.write alternatives**: Secure alternatives to document.write, modern DOM manipulation techniques
|
||||
|
||||
### Content Security Policy (CSP)
|
||||
- **CSP header configuration**: Directive setup, policy refinement, report-only mode implementation
|
||||
- **Script source restrictions**: nonce-based CSP, hash-based CSP, strict-dynamic policies
|
||||
- **Inline script elimination**: Moving inline scripts to external files, event handler security
|
||||
- **Style source control**: CSS nonce implementation, style-src directives, unsafe-inline alternatives
|
||||
- **Report collection**: CSP violation reporting, monitoring and alerting on policy violations
|
||||
- **Progressive CSP deployment**: Gradual CSP tightening, compatibility testing, fallback strategies
|
||||
|
||||
### Input Validation and Sanitization
|
||||
- **Client-side validation**: Form validation security, input pattern enforcement, data type validation
|
||||
- **Allowlist validation**: Whitelist-based input validation, predefined value sets, enumeration security
|
||||
- **Regular expression security**: Safe regex patterns, ReDoS prevention, input format validation
|
||||
- **File upload security**: File type validation, size restrictions, virus scanning integration
|
||||
- **URL validation**: Link validation, protocol restrictions, malicious URL detection
|
||||
- **Real-time validation**: Secure AJAX validation, rate limiting for validation requests
|
||||
|
||||
### CSS Handling Security
|
||||
- **Dynamic style sanitization**: CSS property validation, style injection prevention, safe CSS generation
|
||||
- **Inline style alternatives**: External stylesheet usage, CSS-in-JS security, style encapsulation
|
||||
- **CSS injection prevention**: Style property validation, CSS expression prevention, browser-specific protections
|
||||
- **CSP style integration**: style-src directives, nonce-based styles, hash-based style validation
|
||||
- **CSS custom properties**: Secure CSS variable usage, property sanitization, dynamic theming security
|
||||
- **Third-party CSS**: External stylesheet validation, subresource integrity for stylesheets
|
||||
|
||||
### Clickjacking Protection
|
||||
- **Frame detection**: Intersection Observer API implementation, UI overlay detection, frame-busting logic
|
||||
- **Frame-busting techniques**: JavaScript-based frame busting, top-level navigation protection
|
||||
- **X-Frame-Options**: DENY and SAMEORIGIN implementation, frame ancestor control
|
||||
- **CSP frame-ancestors**: Content Security Policy frame protection, granular frame source control
|
||||
- **SameSite cookie protection**: Cross-frame CSRF protection, cookie isolation techniques
|
||||
- **Visual confirmation**: User action confirmation, critical operation verification, overlay detection
|
||||
- **Environment-specific deployment**: Apply clickjacking protection only in production or standalone applications, disable or relax during development when embedding in iframes
|
||||
|
||||
### Secure Redirects and Navigation
|
||||
- **Redirect validation**: URL allowlist validation, internal redirect verification, domain allowlist enforcement
|
||||
- **Open redirect prevention**: Parameterized redirect protection, fixed destination mapping, identifier-based redirects
|
||||
- **URL manipulation security**: Query parameter validation, fragment handling, URL construction security
|
||||
- **History API security**: Secure state management, navigation event handling, URL spoofing prevention
|
||||
- **External link handling**: rel="noopener noreferrer" implementation, target="_blank" security
|
||||
- **Deep link validation**: Route parameter validation, path traversal prevention, authorization checks
|
||||
|
||||
### Authentication and Session Management
|
||||
- **Token storage**: Secure JWT storage, localStorage vs sessionStorage security, token refresh handling
|
||||
- **Session timeout**: Automatic logout implementation, activity monitoring, session extension security
|
||||
- **Multi-tab synchronization**: Cross-tab session management, storage event handling, logout propagation
|
||||
- **Biometric authentication**: WebAuthn implementation, FIDO2 integration, fallback authentication
|
||||
- **OAuth client security**: PKCE implementation, state parameter validation, authorization code handling
|
||||
- **Password handling**: Secure password fields, password visibility toggles, form auto-completion security
|
||||
|
||||
### Browser Security Features
|
||||
- **Subresource Integrity (SRI)**: CDN resource validation, integrity hash generation, fallback mechanisms
|
||||
- **Trusted Types**: DOM sink protection, policy configuration, trusted HTML generation
|
||||
- **Feature Policy**: Browser feature restrictions, permission management, capability control
|
||||
- **HTTPS enforcement**: Mixed content prevention, secure cookie handling, protocol upgrade enforcement
|
||||
- **Referrer Policy**: Information leakage prevention, referrer header control, privacy protection
|
||||
- **Cross-Origin policies**: CORP and COEP implementation, cross-origin isolation, shared array buffer security
|
||||
|
||||
### Third-Party Integration Security
|
||||
- **CDN security**: Subresource integrity, CDN fallback strategies, third-party script validation
|
||||
- **Widget security**: Iframe sandboxing, postMessage security, cross-frame communication protocols
|
||||
- **Analytics security**: Privacy-preserving analytics, data collection minimization, consent management
|
||||
- **Social media integration**: OAuth security, API key protection, user data handling
|
||||
- **Payment integration**: PCI compliance, tokenization, secure payment form handling
|
||||
- **Chat and support widgets**: XSS prevention in chat interfaces, message sanitization, content filtering
|
||||
|
||||
### Progressive Web App Security
|
||||
- **Service Worker security**: Secure caching strategies, update mechanisms, worker isolation
|
||||
- **Web App Manifest**: Secure manifest configuration, deep link handling, app installation security
|
||||
- **Push notifications**: Secure notification handling, permission management, payload validation
|
||||
- **Offline functionality**: Secure offline storage, data synchronization security, conflict resolution
|
||||
- **Background sync**: Secure background operations, data integrity, privacy considerations
|
||||
|
||||
### Mobile and Responsive Security
|
||||
- **Touch interaction security**: Gesture validation, touch event security, haptic feedback
|
||||
- **Viewport security**: Secure viewport configuration, zoom prevention for sensitive forms
|
||||
- **Device API security**: Geolocation privacy, camera/microphone permissions, sensor data protection
|
||||
- **App-like behavior**: PWA security, full-screen mode security, navigation gesture handling
|
||||
- **Cross-platform compatibility**: Platform-specific security considerations, feature detection security
|
||||
|
||||
## Behavioral Traits
|
||||
- Always prefers textContent over innerHTML for dynamic content
|
||||
- Implements comprehensive input validation with allowlist approaches
|
||||
- Uses Content Security Policy headers to prevent script injection
|
||||
- Validates all user-supplied URLs before navigation or redirects
|
||||
- Applies frame-busting techniques only in production environments
|
||||
- Sanitizes all dynamic content with established libraries like DOMPurify
|
||||
- Implements secure authentication token storage and management
|
||||
- Uses modern browser security features and APIs
|
||||
- Considers privacy implications in all user interactions
|
||||
- Maintains separation between trusted and untrusted content
|
||||
|
||||
## Knowledge Base
|
||||
- XSS prevention techniques and DOM security patterns
|
||||
- Content Security Policy implementation and configuration
|
||||
- Browser security features and APIs
|
||||
- Input validation and sanitization best practices
|
||||
- Clickjacking and UI redressing attack prevention
|
||||
- Secure authentication and session management patterns
|
||||
- Third-party integration security considerations
|
||||
- Progressive Web App security implementation
|
||||
- Modern browser security headers and policies
|
||||
- Client-side vulnerability assessment and mitigation
|
||||
|
||||
## Response Approach
|
||||
1. **Assess client-side security requirements** including threat model and user interaction patterns
|
||||
2. **Implement secure DOM manipulation** using textContent and secure APIs
|
||||
3. **Configure Content Security Policy** with appropriate directives and violation reporting
|
||||
4. **Validate all user inputs** with allowlist-based validation and sanitization
|
||||
5. **Implement clickjacking protection** with frame detection and busting techniques
|
||||
6. **Secure navigation and redirects** with URL validation and allowlist enforcement
|
||||
7. **Apply browser security features** including SRI, Trusted Types, and security headers
|
||||
8. **Handle authentication securely** with proper token storage and session management
|
||||
9. **Test security controls** with both automated scanning and manual verification
|
||||
|
||||
## Example Interactions
|
||||
- "Implement secure DOM manipulation for user-generated content display"
|
||||
- "Configure Content Security Policy to prevent XSS while maintaining functionality"
|
||||
- "Create secure form validation that prevents injection attacks"
|
||||
- "Implement clickjacking protection for sensitive user operations"
|
||||
- "Set up secure redirect handling with URL validation and allowlists"
|
||||
- "Sanitize user input for rich text editor with DOMPurify integration"
|
||||
- "Implement secure authentication token storage and rotation"
|
||||
- "Create secure third-party widget integration with iframe sandboxing"
|
||||
@@ -0,0 +1,481 @@
|
||||
---
|
||||
name: pci-compliance
|
||||
description: "Master PCI DSS (Payment Card Industry Data Security Standard) compliance for secure payment processing and handling of cardholder data."
|
||||
risk: unknown
|
||||
source: community
|
||||
date_added: "2026-02-27"
|
||||
---
|
||||
|
||||
# PCI Compliance
|
||||
|
||||
Master PCI DSS (Payment Card Industry Data Security Standard) compliance for secure payment processing and handling of cardholder data.
|
||||
|
||||
## Do not use this skill when
|
||||
|
||||
- The task is unrelated to pci compliance
|
||||
- You need a different domain or tool outside this scope
|
||||
|
||||
## Instructions
|
||||
|
||||
- Clarify goals, constraints, and required inputs.
|
||||
- Apply relevant best practices and validate outcomes.
|
||||
- Provide actionable steps and verification.
|
||||
- If detailed examples are required, open `resources/implementation-playbook.md`.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
- Building payment processing systems
|
||||
- Handling credit card information
|
||||
- Implementing secure payment flows
|
||||
- Conducting PCI compliance audits
|
||||
- Reducing PCI compliance scope
|
||||
- Implementing tokenization and encryption
|
||||
- Preparing for PCI DSS assessments
|
||||
|
||||
## PCI DSS Requirements (12 Core Requirements)
|
||||
|
||||
### Build and Maintain Secure Network
|
||||
1. Install and maintain firewall configuration
|
||||
2. Don't use vendor-supplied defaults for passwords
|
||||
|
||||
### Protect Cardholder Data
|
||||
3. Protect stored cardholder data
|
||||
4. Encrypt transmission of cardholder data across public networks
|
||||
|
||||
### Maintain Vulnerability Management
|
||||
5. Protect systems against malware
|
||||
6. Develop and maintain secure systems and applications
|
||||
|
||||
### Implement Strong Access Control
|
||||
7. Restrict access to cardholder data by business need-to-know
|
||||
8. Identify and authenticate access to system components
|
||||
9. Restrict physical access to cardholder data
|
||||
|
||||
### Monitor and Test Networks
|
||||
10. Track and monitor all access to network resources and cardholder data
|
||||
11. Regularly test security systems and processes
|
||||
|
||||
### Maintain Information Security Policy
|
||||
12. Maintain a policy that addresses information security
|
||||
|
||||
## Compliance Levels
|
||||
|
||||
**Level 1**: > 6 million transactions/year (annual ROC required)
|
||||
**Level 2**: 1-6 million transactions/year (annual SAQ)
|
||||
**Level 3**: 20,000-1 million e-commerce transactions/year
|
||||
**Level 4**: < 20,000 e-commerce or < 1 million total transactions
|
||||
|
||||
## Data Minimization (Never Store)
|
||||
|
||||
```python
|
||||
# NEVER STORE THESE
|
||||
PROHIBITED_DATA = {
|
||||
'full_track_data': 'Magnetic stripe data',
|
||||
'cvv': 'Card verification code/value',
|
||||
'pin': 'PIN or PIN block'
|
||||
}
|
||||
|
||||
# CAN STORE (if encrypted)
|
||||
ALLOWED_DATA = {
|
||||
'pan': 'Primary Account Number (card number)',
|
||||
'cardholder_name': 'Name on card',
|
||||
'expiration_date': 'Card expiration',
|
||||
'service_code': 'Service code'
|
||||
}
|
||||
|
||||
class PaymentData:
|
||||
"""Safe payment data handling."""
|
||||
|
||||
def __init__(self):
|
||||
self.prohibited_fields = ['cvv', 'cvv2', 'cvc', 'pin']
|
||||
|
||||
def sanitize_log(self, data):
|
||||
"""Remove sensitive data from logs."""
|
||||
sanitized = data.copy()
|
||||
|
||||
# Mask PAN
|
||||
if 'card_number' in sanitized:
|
||||
card = sanitized['card_number']
|
||||
sanitized['card_number'] = f"{card[:6]}{'*' * (len(card) - 10)}{card[-4:]}"
|
||||
|
||||
# Remove prohibited data
|
||||
for field in self.prohibited_fields:
|
||||
sanitized.pop(field, None)
|
||||
|
||||
return sanitized
|
||||
|
||||
def validate_no_prohibited_storage(self, data):
|
||||
"""Ensure no prohibited data is being stored."""
|
||||
for field in self.prohibited_fields:
|
||||
if field in data:
|
||||
raise SecurityError(f"Attempting to store prohibited field: {field}")
|
||||
```
|
||||
|
||||
## Tokenization
|
||||
|
||||
### Using Payment Processor Tokens
|
||||
```python
|
||||
import stripe
|
||||
|
||||
class TokenizedPayment:
|
||||
"""Handle payments using tokens (no card data on server)."""
|
||||
|
||||
@staticmethod
|
||||
def create_payment_method_token(card_details):
|
||||
"""Create token from card details (client-side only)."""
|
||||
# THIS SHOULD ONLY BE DONE CLIENT-SIDE WITH STRIPE.JS
|
||||
# NEVER send card details to your server
|
||||
|
||||
"""
|
||||
// Frontend JavaScript
|
||||
const stripe = Stripe('pk_...');
|
||||
|
||||
const {token, error} = await stripe.createToken({
|
||||
card: {
|
||||
number: '4242424242424242',
|
||||
exp_month: 12,
|
||||
exp_year: 2024,
|
||||
cvc: '123'
|
||||
}
|
||||
});
|
||||
|
||||
// Send token.id to server (NOT card details)
|
||||
"""
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def charge_with_token(token_id, amount):
|
||||
"""Charge using token (server-side)."""
|
||||
# Your server only sees the token, never the card number
|
||||
stripe.api_key = "sk_..."
|
||||
|
||||
charge = stripe.Charge.create(
|
||||
amount=amount,
|
||||
currency="usd",
|
||||
source=token_id, # Token instead of card details
|
||||
description="Payment"
|
||||
)
|
||||
|
||||
return charge
|
||||
|
||||
@staticmethod
|
||||
def store_payment_method(customer_id, payment_method_token):
|
||||
"""Store payment method as token for future use."""
|
||||
stripe.Customer.modify(
|
||||
customer_id,
|
||||
source=payment_method_token
|
||||
)
|
||||
|
||||
# Store only customer_id and payment_method_id in your database
|
||||
# NEVER store actual card details
|
||||
return {
|
||||
'customer_id': customer_id,
|
||||
'has_payment_method': True
|
||||
# DO NOT store: card number, CVV, etc.
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Tokenization (Advanced)
|
||||
```python
|
||||
import secrets
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
class TokenVault:
|
||||
"""Secure token vault for card data (if you must store it)."""
|
||||
|
||||
def __init__(self, encryption_key):
|
||||
self.cipher = Fernet(encryption_key)
|
||||
self.vault = {} # In production: use encrypted database
|
||||
|
||||
def tokenize(self, card_data):
|
||||
"""Convert card data to token."""
|
||||
# Generate secure random token
|
||||
token = secrets.token_urlsafe(32)
|
||||
|
||||
# Encrypt card data
|
||||
encrypted = self.cipher.encrypt(json.dumps(card_data).encode())
|
||||
|
||||
# Store token -> encrypted data mapping
|
||||
self.vault[token] = encrypted
|
||||
|
||||
return token
|
||||
|
||||
def detokenize(self, token):
|
||||
"""Retrieve card data from token."""
|
||||
encrypted = self.vault.get(token)
|
||||
if not encrypted:
|
||||
raise ValueError("Token not found")
|
||||
|
||||
# Decrypt
|
||||
decrypted = self.cipher.decrypt(encrypted)
|
||||
return json.loads(decrypted.decode())
|
||||
|
||||
def delete_token(self, token):
|
||||
"""Remove token from vault."""
|
||||
self.vault.pop(token, None)
|
||||
```
|
||||
|
||||
## Encryption
|
||||
|
||||
### Data at Rest
|
||||
```python
|
||||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||
import os
|
||||
|
||||
class EncryptedStorage:
|
||||
"""Encrypt data at rest using AES-256-GCM."""
|
||||
|
||||
def __init__(self, encryption_key):
|
||||
"""Initialize with 256-bit key."""
|
||||
self.key = encryption_key # Must be 32 bytes
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
"""Encrypt data."""
|
||||
# Generate random nonce
|
||||
nonce = os.urandom(12)
|
||||
|
||||
# Encrypt
|
||||
aesgcm = AESGCM(self.key)
|
||||
ciphertext = aesgcm.encrypt(nonce, plaintext.encode(), None)
|
||||
|
||||
# Return nonce + ciphertext
|
||||
return nonce + ciphertext
|
||||
|
||||
def decrypt(self, encrypted_data):
|
||||
"""Decrypt data."""
|
||||
# Extract nonce and ciphertext
|
||||
nonce = encrypted_data[:12]
|
||||
ciphertext = encrypted_data[12:]
|
||||
|
||||
# Decrypt
|
||||
aesgcm = AESGCM(self.key)
|
||||
plaintext = aesgcm.decrypt(nonce, ciphertext, None)
|
||||
|
||||
return plaintext.decode()
|
||||
|
||||
# Usage
|
||||
storage = EncryptedStorage(os.urandom(32))
|
||||
encrypted_pan = storage.encrypt("4242424242424242")
|
||||
# Store encrypted_pan in database
|
||||
```
|
||||
|
||||
### Data in Transit
|
||||
```python
|
||||
# Always use TLS 1.2 or higher
|
||||
# Flask/Django example
|
||||
app.config['SESSION_COOKIE_SECURE'] = True # HTTPS only
|
||||
app.config['SESSION_COOKIE_HTTPONLY'] = True
|
||||
app.config['SESSION_COOKIE_SAMESITE'] = 'Strict'
|
||||
|
||||
# Enforce HTTPS
|
||||
from flask_talisman import Talisman
|
||||
Talisman(app, force_https=True)
|
||||
```
|
||||
|
||||
## Access Control
|
||||
|
||||
```python
|
||||
from functools import wraps
|
||||
from flask import session
|
||||
|
||||
def require_pci_access(f):
|
||||
"""Decorator to restrict access to cardholder data."""
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
user = session.get('user')
|
||||
|
||||
# Check if user has PCI access role
|
||||
if not user or 'pci_access' not in user.get('roles', []):
|
||||
return {'error': 'Unauthorized access to cardholder data'}, 403
|
||||
|
||||
# Log access attempt
|
||||
audit_log(
|
||||
user=user['id'],
|
||||
action='access_cardholder_data',
|
||||
resource=f.__name__
|
||||
)
|
||||
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return decorated_function
|
||||
|
||||
@app.route('/api/payment-methods')
|
||||
@require_pci_access
|
||||
def get_payment_methods():
|
||||
"""Retrieve payment methods (restricted access)."""
|
||||
# Only accessible to users with pci_access role
|
||||
pass
|
||||
```
|
||||
|
||||
## Audit Logging
|
||||
|
||||
```python
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
class PCIAuditLogger:
|
||||
"""PCI-compliant audit logging."""
|
||||
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger('pci_audit')
|
||||
# Configure to write to secure, append-only log
|
||||
|
||||
def log_access(self, user_id, resource, action, result):
|
||||
"""Log access to cardholder data."""
|
||||
entry = {
|
||||
'timestamp': datetime.utcnow().isoformat(),
|
||||
'user_id': user_id,
|
||||
'resource': resource,
|
||||
'action': action,
|
||||
'result': result,
|
||||
'ip_address': request.remote_addr
|
||||
}
|
||||
|
||||
self.logger.info(json.dumps(entry))
|
||||
|
||||
def log_authentication(self, user_id, success, method):
|
||||
"""Log authentication attempt."""
|
||||
entry = {
|
||||
'timestamp': datetime.utcnow().isoformat(),
|
||||
'user_id': user_id,
|
||||
'event': 'authentication',
|
||||
'success': success,
|
||||
'method': method,
|
||||
'ip_address': request.remote_addr
|
||||
}
|
||||
|
||||
self.logger.info(json.dumps(entry))
|
||||
|
||||
# Usage
|
||||
audit = PCIAuditLogger()
|
||||
audit.log_access(user_id=123, resource='payment_methods', action='read', result='success')
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### Input Validation
|
||||
```python
|
||||
import re
|
||||
|
||||
def validate_card_number(card_number):
|
||||
"""Validate card number format (Luhn algorithm)."""
|
||||
# Remove spaces and dashes
|
||||
card_number = re.sub(r'[\s-]', '', card_number)
|
||||
|
||||
# Check if all digits
|
||||
if not card_number.isdigit():
|
||||
return False
|
||||
|
||||
# Luhn algorithm
|
||||
def luhn_checksum(card_num):
|
||||
def digits_of(n):
|
||||
return [int(d) for d in str(n)]
|
||||
|
||||
digits = digits_of(card_num)
|
||||
odd_digits = digits[-1::-2]
|
||||
even_digits = digits[-2::-2]
|
||||
checksum = sum(odd_digits)
|
||||
for d in even_digits:
|
||||
checksum += sum(digits_of(d * 2))
|
||||
return checksum % 10
|
||||
|
||||
return luhn_checksum(card_number) == 0
|
||||
|
||||
def sanitize_input(user_input):
|
||||
"""Sanitize user input to prevent injection."""
|
||||
# Remove special characters
|
||||
# Validate against expected format
|
||||
# Escape for database queries
|
||||
pass
|
||||
```
|
||||
|
||||
## PCI DSS SAQ (Self-Assessment Questionnaire)
|
||||
|
||||
### SAQ A (Least Requirements)
|
||||
- E-commerce using hosted payment page
|
||||
- No card data on your systems
|
||||
- ~20 questions
|
||||
|
||||
### SAQ A-EP
|
||||
- E-commerce with embedded payment form
|
||||
- Uses JavaScript to handle card data
|
||||
- ~180 questions
|
||||
|
||||
### SAQ D (Most Requirements)
|
||||
- Store, process, or transmit card data
|
||||
- Full PCI DSS requirements
|
||||
- ~300 questions
|
||||
|
||||
## Compliance Checklist
|
||||
|
||||
```python
|
||||
PCI_COMPLIANCE_CHECKLIST = {
|
||||
'network_security': [
|
||||
'Firewall configured and maintained',
|
||||
'No vendor default passwords',
|
||||
'Network segmentation implemented'
|
||||
],
|
||||
'data_protection': [
|
||||
'No storage of CVV, track data, or PIN',
|
||||
'PAN encrypted when stored',
|
||||
'PAN masked when displayed',
|
||||
'Encryption keys properly managed'
|
||||
],
|
||||
'vulnerability_management': [
|
||||
'Anti-virus installed and updated',
|
||||
'Secure development practices',
|
||||
'Regular security patches',
|
||||
'Vulnerability scanning performed'
|
||||
],
|
||||
'access_control': [
|
||||
'Access restricted by role',
|
||||
'Unique IDs for all users',
|
||||
'Multi-factor authentication',
|
||||
'Physical security measures'
|
||||
],
|
||||
'monitoring': [
|
||||
'Audit logs enabled',
|
||||
'Log review process',
|
||||
'File integrity monitoring',
|
||||
'Regular security testing'
|
||||
],
|
||||
'policy': [
|
||||
'Security policy documented',
|
||||
'Risk assessment performed',
|
||||
'Security awareness training',
|
||||
'Incident response plan'
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- **references/data-minimization.md**: Never store prohibited data
|
||||
- **references/tokenization.md**: Tokenization strategies
|
||||
- **references/encryption.md**: Encryption requirements
|
||||
- **references/access-control.md**: Role-based access
|
||||
- **references/audit-logging.md**: Comprehensive logging
|
||||
- **assets/pci-compliance-checklist.md**: Complete checklist
|
||||
- **assets/encrypted-storage.py**: Encryption utilities
|
||||
- **scripts/audit-payment-system.sh**: Compliance audit script
|
||||
|
||||
## Common Violations
|
||||
|
||||
1. **Storing CVV**: Never store card verification codes
|
||||
2. **Unencrypted PAN**: Card numbers must be encrypted at rest
|
||||
3. **Weak Encryption**: Use AES-256 or equivalent
|
||||
4. **No Access Controls**: Restrict who can access cardholder data
|
||||
5. **Missing Audit Logs**: Must log all access to payment data
|
||||
6. **Insecure Transmission**: Always use TLS 1.2+
|
||||
7. **Default Passwords**: Change all default credentials
|
||||
8. **No Security Testing**: Regular penetration testing required
|
||||
|
||||
## Reducing PCI Scope
|
||||
|
||||
1. **Use Hosted Payments**: Stripe Checkout, PayPal, etc.
|
||||
2. **Tokenization**: Replace card data with tokens
|
||||
3. **Network Segmentation**: Isolate cardholder data environment
|
||||
4. **Outsource**: Use PCI-compliant payment processors
|
||||
5. **No Storage**: Never store full card details
|
||||
|
||||
By minimizing systems that touch card data, you reduce compliance burden significantly.
|
||||
Reference in New Issue
Block a user