api-security-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAPI Security Best Practices
API安全最佳实践
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.
指导开发者通过实现身份验证、授权、输入验证、速率限制以及针对常见漏洞的防护来构建安全的API。本技能涵盖REST、GraphQL和WebSocket API的安全模式。
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
- 设计新API端点时使用
- 加固现有API时使用
- 实现身份验证与授权机制时使用
- 防护API攻击(注入、DDoS等)时使用
- 开展API安全评审时使用
- 准备安全审计时使用
- 实现速率限制与流量控制时使用
- 在API中处理敏感数据时使用
How It Works
工作流程
Step 1: Authentication & Authorization
步骤1:身份验证与授权
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)
我会帮助你实现安全的身份验证机制:
- 选择身份验证方式(JWT、OAuth 2.0、API密钥)
- 实现基于令牌的身份验证
- 搭建基于角色的访问控制(RBAC)
- 安全的会话管理
- 实现多因素身份验证(MFA)
Step 2: Input Validation & Sanitization
步骤2:输入验证与清理
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
防护注入攻击:
- 验证所有输入数据
- 清理用户输入
- 使用参数化查询
- 实现请求 schema 验证
- 防范SQL注入、XSS和命令注入
Step 3: Rate Limiting & Throttling
步骤3:速率限制与流量控制
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
防止滥用和DDoS攻击:
- 按用户/IP实现速率限制
- 搭建API流量控制
- 配置请求配额
- 优雅处理速率限制错误
- 监控可疑活动
Step 4: Data Protection
步骤4:数据保护
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
保护敏感数据:
- 加密传输中的数据(HTTPS/TLS)
- 加密静态存储的敏感数据
- 实现恰当的错误处理(避免数据泄露)
- 清理错误信息
- 使用安全HTTP头
Step 5: API Security Testing
步骤5:API安全测试
Verify security implementation:
- Test authentication and authorization
- Perform penetration testing
- Check for common vulnerabilities (OWASP API Top 10)
- Validate input handling
- Test rate limiting
验证安全实现效果:
- 测试身份验证与授权机制
- 执行渗透测试
- 检查常见漏洞(OWASP API Top 10)
- 验证输入处理逻辑
- 测试速率限制功能
Examples
示例
Example 1: Implementing JWT Authentication
示例1:实现JWT身份验证
markdown
undefinedmarkdown
undefinedSecure JWT Authentication Implementation
安全的JWT身份验证实现
Authentication Flow
身份验证流程
- User logs in with credentials
- Server validates credentials
- Server generates JWT token
- Client stores token securely
- Client sends token with each request
- Server validates token
- 用户使用凭证登录
- 服务器验证凭证
- 服务器生成JWT令牌
- 客户端安全存储令牌
- 客户端在每个请求中携带令牌
- 服务器验证令牌
Implementation
实现代码
1. Generate Secure JWT Tokens
1. 生成安全的JWT令牌
```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'
});
}
});
```
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)
2. 验证JWT令牌(中间件)
```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 };
```
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
3. 保护路由
```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' });
}
});
```
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
4. 实现令牌刷新
```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'
});
}
});
```
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
undefined- ✅ 使用强JWT密钥(至少256位)
- ✅ 设置短过期时间(访问令牌1小时)
- ✅ 实现刷新令牌以支持长会话
- ✅ 在数据库中存储刷新令牌(可撤销)
- ✅ 仅使用HTTPS
- ✅ 不要在JWT负载中存储敏感数据
- ✅ 验证令牌的发行者和受众
- ✅ 实现令牌黑名单以支持登出
undefinedExample 2: Input Validation and SQL Injection Prevention
示例2:输入验证与SQL注入防护
markdown
undefinedmarkdown
undefinedPreventing SQL Injection and Input Validation
防范SQL注入与输入验证
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!
```
❌ 存在漏洞的代码:
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
1. 使用参数化查询
```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);
});
```
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
2. 使用带自动转义的ORM
```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);
});
```
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
3. 使用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);}
);
```
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
4. 清理输出以防范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);
});
```
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
undefined- 验证所有用户输入
- 使用参数化查询或ORM
- 验证数据类型(字符串、数字、邮箱等)
- 验证数据范围(最小/最大长度、值范围)
- 清理HTML内容
- 转义特殊字符
- 验证文件上传(类型、大小、内容)
- 使用允许列表,而非阻止列表
undefinedExample 3: Rate Limiting and DDoS Protection
示例3:速率限制与DDoS防护
markdown
undefinedmarkdown
undefinedImplementing Rate Limiting
实现速率限制
Why Rate Limiting?
为什么需要速率限制?
- Prevent brute force attacks
- Protect against DDoS
- Prevent API abuse
- Ensure fair usage
- Reduce server costs
- 防范暴力破解攻击
- 防护DDoS攻击
- 防止API滥用
- 确保公平使用
- 降低服务器成本
Implementation with Express Rate Limit
使用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
}
);
```
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());
```
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
使用Helmet实现DDoS防护
```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
}
}));
```
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
```
undefinedX-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1640000000
Retry-After: 900undefinedBest 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
- 全程使用HTTPS - 绝不要通过HTTP传输敏感数据
- 实现身份验证 - 受保护的端点必须要求身份验证
- 验证所有输入 - 绝不信任用户输入
- 使用参数化查询 - 防范SQL注入
- 实现速率限制 - 防护暴力破解和DDoS攻击
- 哈希密码 - 使用bcrypt,盐轮次≥10
- 使用短期令牌 - JWT访问令牌应快速过期
- 正确实现CORS - 仅允许可信来源
- 记录安全事件 - 监控可疑活动
- 保持依赖更新 - 定期更新包
- 使用安全HTTP头 - 实现Helmet.js
- 清理错误信息 - 不要泄露敏感信息
❌ 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
- 不要明文存储密码 - 始终哈希密码
- 不要使用弱密钥 - 使用强随机JWT密钥
- 不要信任用户输入 - 始终验证和清理
- 不要暴露堆栈跟踪 - 生产环境隐藏错误详情
- 不要使用字符串拼接构建SQL - 使用参数化查询
- 不要在JWT中存储敏感数据 - JWT未加密
- 不要忽略安全更新 - 定期更新依赖
- 不要使用默认凭证 - 修改所有默认密码
- 不要完全禁用CORS - 正确配置而非禁用
- 不要记录敏感数据 - 清理日志内容
Common Pitfalls
常见陷阱
Problem: JWT Secret Exposed in Code
问题:JWT密钥在代码中暴露
Symptoms: JWT secret hardcoded or committed to Git
Solution:
```javascript
// ❌ Bad
const tokenSigningKey = '[redacted weak value]';
// ✅ 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'))"
```
症状: JWT密钥硬编码或提交到Git
解决方案:
javascript
// ❌ Bad
const tokenSigningKey = '[redacted weak value]';
// ✅ 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
});
}
```
症状: 用户可以设置如"password123"的弱密码
解决方案:
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 });
});
```
症状: 用户可以访问不属于自己的资源
解决方案:
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'
});}
});
```
症状: 错误信息泄露系统细节
解决方案:
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)
- 实现强身份验证(JWT、OAuth 2.0)
- 所有端点使用HTTPS
- 使用bcrypt哈希密码(盐轮次≥10)
- 实现令牌过期机制
- 添加刷新令牌机制
- 每个请求都验证用户授权
- 实现基于角色的访问控制(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
- 验证所有用户输入
- 使用参数化查询或ORM
- 清理HTML内容
- 验证文件上传
- 实现请求schema验证
- 使用允许列表,而非阻止列表
Rate Limiting & DDoS Protection
速率限制与DDoS防护
- 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
- 按用户/IP实现速率限制
- 为认证端点设置更严格的限制
- 使用Redis实现分布式速率限制
- 返回正确的速率限制响应头
- 实现请求流量控制
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)
- 所有流量使用HTTPS/TLS
- 加密静态存储的敏感数据
- 不要在JWT中存储敏感数据
- 清理错误信息
- 正确配置CORS
- 使用安全HTTP头(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
- 记录安全事件
- 监控可疑活动
- 为失败的认证尝试设置告警
- 跟踪API使用模式
- 不要记录敏感数据
OWASP API Security Top 10
OWASP API安全Top 10
- Broken Object Level Authorization - Always verify user can access resource
- Broken Authentication - Implement strong authentication mechanisms
- Broken Object Property Level Authorization - Validate which properties user can access
- Unrestricted Resource Consumption - Implement rate limiting and quotas
- Broken Function Level Authorization - Verify user role for each function
- Unrestricted Access to Sensitive Business Flows - Protect critical workflows
- Server Side Request Forgery (SSRF) - Validate and sanitize URLs
- Security Misconfiguration - Use security best practices and headers
- Improper Inventory Management - Document and secure all API endpoints
- Unsafe Consumption of APIs - Validate data from third-party APIs
- 对象级授权失效 - 始终验证用户是否有权访问资源
- 身份验证机制失效 - 实现强身份验证机制
- 对象属性级授权失效 - 验证用户可访问的属性
- 资源消耗无限制 - 实现速率限制与配额
- 功能级授权失效 - 验证每个功能的用户角色
- 敏感业务流程无限制访问 - 保护关键工作流
- 服务器端请求伪造(SSRF) - 验证和清理URL
- 安全配置错误 - 使用安全最佳实践和HTTP头
- 资产管理不当 - 记录并保护所有API端点
- 不安全的API消费 - 验证第三方API的数据
Related Skills
相关技能
- - Security testing perspective
@ethical-hacking-methodology - - Testing for SQL injection
@sql-injection-testing - - Testing for XSS vulnerabilities
@xss-html-injection - - Authentication vulnerabilities
@broken-authentication - - Backend development standards
@backend-dev-guidelines - - Debug security issues
@systematic-debugging
- - 安全测试视角
@ethical-hacking-methodology - - SQL注入测试
@sql-injection-testing - - XSS漏洞测试
@xss-html-injection - - 身份验证漏洞
@broken-authentication - - 后端开发标准
@backend-dev-guidelines - - 调试安全问题
@systematic-debugging
Additional Resources
额外资源
- OWASP API Security Top 10
- JWT Best Practices
- Express Security Best Practices
- Node.js Security Checklist
- 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!
- OWASP API Security Top 10
- JWT Best Practices
- Express Security Best Practices
- Node.js Security Checklist
- API Security Checklist
专业提示: 安全不是一次性任务 - 定期审计你的API,保持依赖更新,并及时了解新漏洞!
Limitations
局限性
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
- 仅当任务明确符合上述描述的范围时使用本技能。
- 不要将输出视为环境特定验证、测试或专家评审的替代品。
- 如果缺少必要的输入、权限、安全边界或成功标准,请停止并请求澄清。