security-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSecurity Best Practices
安全最佳实践
When to use this skill
适用场景
- 신규 프로젝트: 처음부터 보안 고려
- 보안 감사: 취약점 점검 및 수정
- API 공개: 외부 접근 API 보안 강화
- 컴플라이언스: GDPR, PCI-DSS 등 준수
- 新项目:从初始阶段就考虑安全
- 安全审计:漏洞检查与修复
- 公开API:强化外部可访问API的安全性
- 合规性:满足GDPR、PCI-DSS等合规要求
Instructions
实施步骤
Step 1: HTTPS 강제 및 보안 헤더
步骤1:强制HTTPS与安全头设置
Express.js 보안 미들웨어:
typescript
import express from 'express';
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
const app = express();
// Helmet: 보안 헤더 자동 설정
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "https://trusted-cdn.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "https://api.example.com"],
fontSrc: ["'self'", "https:", "data:"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));
// HTTPS 강제
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production' && !req.secure) {
return res.redirect(301, `https://${req.headers.host}${req.url}`);
}
next();
});
// Rate Limiting (DDoS 방지)
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15분
max: 100, // IP당 최대 100 요청
message: 'Too many requests from this IP, please try again later.',
standardHeaders: true,
legacyHeaders: false,
});
app.use('/api/', limiter);
// Auth 엔드포인트는 더 엄격하게
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5, // 15분에 5번만
skipSuccessfulRequests: true // 성공 요청은 카운트하지 않음
});
app.use('/api/auth/login', authLimiter);Express.js安全中间件:
typescript
import express from 'express';
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
const app = express();
// Helmet: 自动设置安全头
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "https://trusted-cdn.com"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "https://api.example.com"],
fontSrc: ["'self'", "https:", "data:"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));
// 强制HTTPS
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production' && !req.secure) {
return res.redirect(301, `https://${req.headers.host}${req.url}`);
}
next();
});
// 速率限制(防止DDoS)
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 每个IP最多100次请求
message: 'Too many requests from this IP, please try again later.',
standardHeaders: true,
legacyHeaders: false,
});
app.use('/api/', limiter);
// 认证端点限制更严格
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5, // 15分钟内最多5次
skipSuccessfulRequests: true // 成功请求不计数
});
app.use('/api/auth/login', authLimiter);Step 2: Input Validation (SQL Injection, XSS 방지)
步骤2:输入验证(防止SQL注入、XSS)
Joi 검증:
typescript
import Joi from 'joi';
const userSchema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().min(8).pattern(/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/).required(),
name: Joi.string().min(2).max(50).required()
});
app.post('/api/users', async (req, res) => {
// 1. Input 검증
const { error, value } = userSchema.validate(req.body);
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
// 2. SQL Injection 방지: Parameterized Queries
// ❌ 나쁜 예
// db.query(`SELECT * FROM users WHERE email = '${email}'`);
// ✅ 좋은 예
const user = await db.query('SELECT * FROM users WHERE email = ?', [value.email]);
// 3. XSS 방지: Output Encoding
// React/Vue는 자동으로 escape, 그 외는 라이브러리 사용
import DOMPurify from 'isomorphic-dompurify';
const sanitized = DOMPurify.sanitize(userInput);
res.json({ user: sanitized });
});Joi验证:
typescript
import Joi from 'joi';
const userSchema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().min(8).pattern(/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/).required(),
name: Joi.string().min(2).max(50).required()
});
app.post('/api/users', async (req, res) => {
// 1. 输入验证
const { error, value } = userSchema.validate(req.body);
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
// 2. 防止SQL注入:参数化查询
// ❌ 错误示例
// db.query(`SELECT * FROM users WHERE email = '${email}'`);
// ✅ 正确示例
const user = await db.query('SELECT * FROM users WHERE email = ?', [value.email]);
// 3. 防止XSS:输出编码
// React/Vue会自动转义,其他场景使用库处理
import DOMPurify from 'isomorphic-dompurify';
const sanitized = DOMPurify.sanitize(userInput);
res.json({ user: sanitized });
});Step 3: CSRF 방지
步骤3:CSRF防护
CSRF Token:
typescript
import csrf from 'csurf';
import cookieParser from 'cookie-parser';
app.use(cookieParser());
// CSRF protection
const csrfProtection = csrf({ cookie: true });
// CSRF 토큰 제공
app.get('/api/csrf-token', csrfProtection, (req, res) => {
res.json({ csrfToken: req.csrfToken() });
});
// 모든 POST/PUT/DELETE 요청에 CSRF 검증
app.post('/api/*', csrfProtection, (req, res, next) => {
next();
});
// 클라이언트에서 사용
// fetch('/api/users', {
// method: 'POST',
// headers: {
// 'CSRF-Token': csrfToken
// },
// body: JSON.stringify(data)
// });CSRF令牌:
typescript
import csrf from 'csurf';
import cookieParser from 'cookie-parser';
app.use(cookieParser());
// CSRF防护
const csrfProtection = csrf({ cookie: true });
// 提供CSRF令牌
app.get('/api/csrf-token', csrfProtection, (req, res) => {
res.json({ csrfToken: req.csrfToken() });
});
// 所有POST/PUT/DELETE请求验证CSRF
app.post('/api/*', csrfProtection, (req, res, next) => {
next();
});
// 客户端使用示例
// fetch('/api/users', {
// method: 'POST',
// headers: {
// 'CSRF-Token': csrfToken
// },
// body: JSON.stringify(data)
// });Step 4: Secrets 관리
步骤4:密钥管理
.env (절대 커밋하지 않음):
bash
undefined.env(绝对禁止提交到版本库):
bash
undefinedDatabase
Database
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
JWT
JWT
ACCESS_TOKEN_SECRET=your-super-secret-access-token-key-min-32-chars
REFRESH_TOKEN_SECRET=your-super-secret-refresh-token-key-min-32-chars
ACCESS_TOKEN_SECRET=your-super-secret-access-token-key-min-32-chars
REFRESH_TOKEN_SECRET=your-super-secret-refresh-token-key-min-32-chars
API Keys
API Keys
STRIPE_SECRET_KEY=sk_test_xxx
SENDGRID_API_KEY=SG.xxx
**Kubernetes Secrets**:
```yaml
apiVersion: v1
kind: Secret
metadata:
name: myapp-secrets
type: Opaque
stringData:
database-url: postgresql://user:password@postgres:5432/mydb
jwt-secret: your-jwt-secrettypescript
// 환경변수에서 읽기
const dbUrl = process.env.DATABASE_URL;
if (!dbUrl) {
throw new Error('DATABASE_URL environment variable is required');
}STRIPE_SECRET_KEY=sk_test_xxx
SENDGRID_API_KEY=SG.xxx
**Kubernetes密钥**:
```yaml
apiVersion: v1
kind: Secret
metadata:
name: myapp-secrets
type: Opaque
stringData:
database-url: postgresql://user:password@postgres:5432/mydb
jwt-secret: your-jwt-secrettypescript
// 从环境变量读取
const dbUrl = process.env.DATABASE_URL;
if (!dbUrl) {
throw new Error('DATABASE_URL environment variable is required');
}Step 5: API 인증 보안
步骤5:API认证安全
JWT + Refresh Token Rotation:
typescript
// Access Token 짧게 (15분)
const accessToken = jwt.sign({ userId }, ACCESS_SECRET, { expiresIn: '15m' });
// Refresh Token 길게 (7일), DB에 저장
const refreshToken = jwt.sign({ userId }, REFRESH_SECRET, { expiresIn: '7d' });
await db.refreshToken.create({
userId,
token: refreshToken,
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
});
// Refresh Token Rotation: 사용 시마다 새로 발급
app.post('/api/auth/refresh', async (req, res) => {
const { refreshToken } = req.body;
const payload = jwt.verify(refreshToken, REFRESH_SECRET);
// 기존 토큰 무효화
await db.refreshToken.delete({ where: { token: refreshToken } });
// 새 토큰 발급
const newAccessToken = jwt.sign({ userId: payload.userId }, ACCESS_SECRET, { expiresIn: '15m' });
const newRefreshToken = jwt.sign({ userId: payload.userId }, REFRESH_SECRET, { expiresIn: '7d' });
await db.refreshToken.create({
userId: payload.userId,
token: newRefreshToken,
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
});
res.json({ accessToken: newAccessToken, refreshToken: newRefreshToken });
});JWT + 刷新令牌轮换:
typescript
// 访问令牌有效期短(15分钟)
const accessToken = jwt.sign({ userId }, ACCESS_SECRET, { expiresIn: '15m' });
// 刷新令牌有效期长(7天),存储到数据库
const refreshToken = jwt.sign({ userId }, REFRESH_SECRET, { expiresIn: '7d' });
await db.refreshToken.create({
userId,
token: refreshToken,
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
});
// 刷新令牌轮换:每次使用时重新颁发
app.post('/api/auth/refresh', async (req, res) => {
const { refreshToken } = req.body;
const payload = jwt.verify(refreshToken, REFRESH_SECRET);
// 使旧令牌失效
await db.refreshToken.delete({ where: { token: refreshToken } });
// 颁发新令牌
const newAccessToken = jwt.sign({ userId: payload.userId }, ACCESS_SECRET, { expiresIn: '15m' });
const newRefreshToken = jwt.sign({ userId: payload.userId }, REFRESH_SECRET, { expiresIn: '7d' });
await db.refreshToken.create({
userId: payload.userId,
token: newRefreshToken,
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
});
res.json({ accessToken: newAccessToken, refreshToken: newRefreshToken });
});Constraints
约束条件
필수 규칙 (MUST)
必须遵循的规则(MUST)
- HTTPS Only: 프로덕션에서 HTTPS 필수
- Secrets 분리: 환경변수로 관리, 절대 코드에 하드코딩 금지
- Input Validation: 모든 사용자 입력 검증
- Parameterized Queries: SQL Injection 방지
- Rate Limiting: DDoS 방지
- 仅使用HTTPS:生产环境必须强制使用HTTPS
- 密钥分离管理:通过环境变量管理密钥,绝对禁止硬编码到代码中
- 输入验证:对所有用户输入进行验证
- 参数化查询:防止SQL注入
- 速率限制:防止DDoS攻击
금지 사항 (MUST NOT)
禁止事项(MUST NOT)
- eval() 사용 금지: 코드 인젝션 위험
- innerHTML 직접 사용: XSS 위험
- Secrets 커밋: .env 파일 절대 커밋하지 않음
- 禁止使用eval():存在代码注入风险
- 禁止直接使用innerHTML:存在XSS风险
- 禁止提交密钥:绝对禁止提交.env文件到版本库
OWASP Top 10 체크리스트
OWASP Top 10检查清单
markdown
- [ ] A01: Broken Access Control - RBAC, 권한 검증
- [ ] A02: Cryptographic Failures - HTTPS, 암호화
- [ ] A03: Injection - Parameterized Queries, Input Validation
- [ ] A04: Insecure Design - Security by Design
- [ ] A05: Security Misconfiguration - Helmet, 기본 비밀번호 변경
- [ ] A06: Vulnerable Components - npm audit, 정기 업데이트
- [ ] A07: Authentication Failures - 강력한 인증, MFA
- [ ] A08: Data Integrity Failures - 서명 검증, CSRF 방지
- [ ] A09: Logging Failures - 보안 이벤트 로깅
- [ ] A10: SSRF - 외부 요청 검증markdown
- [ ] A01: Broken Access Control - RBAC、权限验证
- [ ] A02: Cryptographic Failures - HTTPS、加密
- [ ] A03: Injection - 参数化查询、输入验证
- [ ] A04: Insecure Design - 安全设计原则
- [ ] A05: Security Misconfiguration - Helmet、默认密码修改
- [ ] A06: Vulnerable Components - npm audit、定期更新
- [ ] A07: Authentication Failures - 强认证、MFA
- [ ] A08: Data Integrity Failures - 签名验证、CSRF防护
- [ ] A09: Logging Failures - 安全事件日志
- [ ] A10: SSRF - 外部请求验证Best practices
最佳实践原则
- Principle of Least Privilege: 최소 권한 부여
- Defense in Depth: 다층 보안
- Security Audits: 정기적인 보안 점검
- 最小权限原则:仅授予必要的最小权限
- 纵深防御:实施多层安全防护
- 定期安全审计:定期开展安全检查
References
参考资料
Metadata
元数据
버전
版本
- 현재 버전: 1.0.0
- 최종 업데이트: 2025-01-01
- 호환 플랫폼: Claude, ChatGPT, Gemini
- 当前版本:1.0.0
- 最后更新:2025-01-01
- 兼容平台:Claude、ChatGPT、Gemini
관련 스킬
相关技能
- authentication-setup
- deployment
- authentication-setup
- deployment
태그
标签
#security#OWASP#HTTPS#CORS#XSS#SQL-injection#CSRF#infrastructure#security#OWASP#HTTPS#CORS#XSS#SQL-injection#CSRF#infrastructureExamples
示例
Example 1: Basic usage
示例1:基础用法
<!-- Add example content here -->
<!-- 添加示例内容 -->
Example 2: Advanced usage
示例2:进阶用法
<!-- Add advanced example content here -->
<!-- 添加进阶示例内容 -->