关于 Security Engineer
Security Engineer 是面向软件开发者的应用安全指南,按认证授权、输入校验、配置、数据保护和监控五个方面整理实践与检查清单。内容还包括常见漏洞防护和部署前审查。
主要功能
- 01认证与授权模式
- 02输入校验与净化
- 03安全配置与密钥管理
- 04数据加密与密码哈希
- 05限流、审计日志与监控
- 06部署前安全检查清单
适用场景
- 01设计 API 的身份验证与权限控制
- 02检查输入处理是否能防范注入和 XSS
- 03审查部署配置与敏感数据保护
- 04准备上线前的安全检查
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ daffy0208/ ai-dev-standards --skill security-engineer安装后在 Claude Code 中输入/security-engineer确认已加载。
附带文件README.mdmanifest.yaml
详细介绍
安全工程师
安全不可妥协——从第一天起就将其融入其中。
核心原则
安全是内建的,而不是事后补上的。
每项功能、每个端点、每条数据流都必须考虑安全影响。在生产环境中修复安全漏洞的成本,是在开发期间修复的 10 倍。
安全的 5 大支柱
支柱 1:身份验证与授权 🔐
身份验证: 你是谁?
授权: 你能做什么?
身份验证策略
JWT(JSON Web Tokens):
- 适用场景: 无状态 API、移动应用、微服务
- 方式: 使用密钥对令牌签名,并将其存储在 httpOnly Cookie 或 Authorization 请求头中
- 安全性: 使用 RS256(而非 HS256),设置较短的有效期(访问令牌 15 分钟,刷新令牌 7 天)
// Example: Next.js API with JWT
import { SignJWT, jwtVerify } from 'jose'
const secret = new TextEncoder().encode(process.env.JWT_SECRET!)
export async function createToken(userId: string) {
return await new SignJWT({ userId })
.setProtectedHeader({ alg: 'HS256' })
.setExpirationTime('15m')
.sign(secret)
}
export async function verifyToken(token: string) {
const { payload } = await jwtVerify(token, secret)
return payload
}
基于会话的身份验证:
- 适用场景: 传统 Web 应用、服务端渲染
- 方式: 服务器在加密 Cookie 中存储会话 ID
- 安全性: 使用 HttpOnly、Secure、SameSite=Strict Cookie
OAuth 2.0 / OIDC:
- 适用场景: 社交登录、第三方集成
- 方式: 使用 NextAuth.js、Passport.js 或 Auth0
- 安全性: 验证 state 参数,移动端使用 PKCE
授权模式
RBAC(基于角色的访问控制):
// Define roles
enum Role {
ADMIN = 'admin',
USER = 'user',
GUEST = 'guest'
}
// Check permissions
function requireRole(allowedRoles: Role[]) {
return (req, res, next) => {
if (!allowedRoles.includes(req.user.role)) {
return res.status(403).json({ error: 'Forbidden' })
}
next()
}
}
// Usage
app.delete('/api/users/:id', requireRole([Role.ADMIN]), deleteUser)
ABAC(基于属性的访问控制):
- 粒度更细:用户创建的资源才允许其编辑
- 示例:只有当 post.authorId === user.id 时,用户才能删除帖子
关键原则:
- ✅ 授权前务必先验证身份
- ✅ 默认拒绝(使用允许列表,而非拒绝列表)
- ✅ 在服务器上检查权限,绝不信任客户端
- ✅ 执行关键操作前重新验证权限
支柱 2:输入验证与清理 🛡️
绝不信任用户输入——验证、清理并转义所有内容。
防止 SQL 注入
❌ 错误(存在漏洞):
// DON'T DO THIS!
const query = `SELECT * FROM users WHERE email = '${userInput}'`
db.query(query) // SQL injection vulnerability!
✅ 正确(参数化查询):
// Always use parameterized queries
const query = 'SELECT * FROM users WHERE email = ?'
db.query(query, [userInput]) // Safe - parameterized
// With Prisma
const user = await prisma.user.findUnique({
where: { email: userInput } // Safe - ORM handles it
})
防止 XSS(跨站脚本攻击)
❌ 错误(存在漏洞):
// DON'T DO THIS!
<div dangerouslySetInnerHTML={{ __html: userInput }} />
✅ 正确(已转义):
// React automatically escapes
;<div>{userInput}</div> // Safe
// If you must render HTML, sanitize first
import DOMPurify from 'isomorphic-dompurify'
;<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />
使用 Zod 验证输入
import { z } from 'zod'
const UserSchema = z.object({
email: z.string().email().max(255),
password: z.string().min(8).max(100),
age: z.number().int().min(13).max(120),
website: z.string().url().optional()
})
// Validate
const result = UserSchema.safeParse(req.body)
if (!result.success) {
return res.status(400).json({ errors: result.error.issues })
}
const validData = result.data // Type-safe!
文件上传安全
import multer from 'multer'
const upload = multer({
limits: {
fileSize: 5 * 1024 * 1024 // 5MB max
},
fileFilter: (req, file, cb) => {
// Whitelist file types
const allowedTypes = ['image/jpeg', 'image/png', 'image/webp']
if (!allowedTypes.includes(file.mimetype)) {
return cb(new Error('Invalid file type'))
}
cb(null, true)
}
})
// Generate random filenames (don't trust user input)
const filename = crypto.randomUUID() + path.extname(file.originalname)
关键原则:
- ✅ 在服务器上进行验证(绝不信任客户端验证)
- ✅ 使用模式验证库(Zod、Yup、Joi)
- ✅ 使用允许列表限定可接受的值,不要使用拒绝列表
- ✅ 根据上下文转义输出(HTML、URL、JS)
支柱 3:安全配置 🔧
环境变量
绝不提交密钥:
# .env (add to .gitignore!)
DATABASE_URL="postgresql://user:pass@localhost:5432/db"
JWT_SECRET="generate-with-openssl-rand-base64-32"
OPENAI_API_KEY="sk-..."
在代码中访问:
// Validate env vars on startup
if (!process.env.JWT_SECRET) {
throw new Error('JWT_SECRET is required')
}
const config = {
jwtSecret: process.env.JWT_SECRET,
databaseUrl: process.env.DATABASE_URL
}
密钥管理(生产环境):
- AWS Secrets Manager
- Vercel Environment Variables
- HashiCorp Vault
- Doppler
安全响应头
// Next.js middleware
export function middleware(request: NextRequest) {
const response = NextResponse.next()
// Prevent clickjacking
response.headers.set('X-Frame-Options', 'DENY')
// Prevent MIME sniffing
response.headers.set('X-Content-Type-Options', 'nosniff')
// XSS Protection
response.headers.set('X-XSS-Protection', '1; mode=block')
// Content Security Policy
response.headers.set(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
)
// HTTPS only
response.headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains')
return response
}
CORS 配置
// Configure CORS properly
app.use(
cors({
origin:
process.env.NODE_ENV === 'production' ? 'https://yourdomain.com' : 'http://localhost:3000',
credentials: true, // Allow cookies
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
})
)
关键原则:
- ✅ 所有密钥都使用环境变量
- ✅ 为每个环境使用不同的密钥(dev、staging、prod)
- ✅ 定期轮换密钥
- ✅ 为所有响应设置安全响应头
- ✅ 严格配置 CORS
支柱 4:数据保护 🔒
密码哈希
❌ 绝不以明文存储密码,也不要使用 MD5/SHA1!
✅ 使用 bcrypt 或 argon2:
import bcrypt from 'bcrypt'
// Hash password (on signup)
const saltRounds = 12
const hashedPassword = await bcrypt.hash(password, saltRounds)
await db.user.create({
email,
password: hashedPassword // Store hash, never plain text
})
// Verify password (on login)
const user = await db.user.findUnique({ where: { email } })
const isValid = await bcrypt.compare(password, user.password)
if (!isValid) {
throw new Error('Invalid credentials')
}
静态数据加密
敏感数据应加密:
import crypto from 'crypto'
const algorithm = 'aes-256-gcm'
const key = Buffer.from(process.env.ENCRYPTION_KEY!, 'hex')
function encrypt(text: string) {
const iv = crypto.randomBytes(16)
const cipher = crypto.createCipheriv(algorithm, key, iv)
const encrypted = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()])
const authTag = cipher.getAuthTag()
return {
iv: iv.toString('hex'),
encryptedData: encrypted.toString('hex'),
authTag: authTag.toString('hex')
}
}
function decrypt(encrypted: any) {
const decipher = crypto.createDecipheriv(algorithm, key, Buffer.from(encrypted.iv, 'hex'))
decipher.setAuthTag(Buffer.from(encrypted.authTag, 'hex'))
return decipher.update(encrypted.encryptedData, 'hex', 'utf8') + decipher.final('utf8')
}
// Encrypt sensitive fields
const ssn = encrypt(user.ssn)
await db.user.update({ where: { id }, data: { ssnEncrypted: JSON.stringify(ssn) } })
传输中加密
始终使用 HTTPS:
- 开发环境:使用
mkcert配置本地 HTTPS - 生产环境:Let's Encrypt、Cloudflare、Vercel(自动启用 HTTPS)
验证外部 API 证书:
// Don't disable SSL verification in production!
fetch(url, {
// DON'T: agent: new https.Agent({ rejectUnauthorized: false })
})
关键原则:
- ✅ 使用 bcrypt(12 轮以上)或 argon2 对密码进行哈希
- ✅ 加密 PII(SSN、信用卡、健康数据)
- ✅ 全程使用 HTTPS(通过 HSTS 响应头强制启用)
- ✅ 加密数据库备份
支柱 5:监控与响应 📊
速率限制
防止暴力破解和 DDoS:
import rateLimit from 'express-rate-limit'
// Global rate limit
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
message: 'Too many requests from this IP'
})
app.use(limiter)
// Stricter limit for auth endpoints
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5, // Only 5 login attempts per 15min
skipSuccessfulRequests: true
})
app.post('/api/auth/login', authLimiter, loginHandler)
审计日志
记录所有与安全相关的事件:
async function auditLog(event: {
userId?: string
action: string
resource: string
ip: string
userAgent: string
success: boolean
metadata?: any
}) {
await db.auditLog.create({
data: {
...event,
timestamp: new Date()
}
})
}
// Usage
await auditLog({
userId: user.id,
action: 'LOGIN',
resource: 'auth',
ip: req.ip,
userAgent: req.headers['user-agent'],
success: true
})
错误处理
不要在错误信息中泄露敏感信息:
❌ 错误:
// Exposes database structure!
catch (error) {
res.status(500).json({ error: error.message })
}
✅ 正确:
catch (error) {
// Log full error server-side
console.error('Error:', error)
// Send generic message to client
res.status(500).json({
error: 'Internal server error',
requestId: generateRequestId() // For support
})
}
安全监控
集成工具:
- Sentry: 错误跟踪、安全警报
- Datadog: APM、异常检测
- CloudWatch: AWS 基础设施监控
- OWASP ZAP: 自动化安全扫描
核心原则:
- ✅ 对所有公共端点实施速率限制
- ✅ 记录身份验证尝试和授权失败
- ✅ 切勿在错误信息中泄露敏感数据
- ✅ 为可疑模式设置警报
- ✅ 定期进行安全审计
OWASP Top 10 快速参考
| # | 漏洞 | 防护措施 |
|---|---|---|
| 1 | 访问控制失效 | 在服务端验证权限,默认拒绝 |
| 2 | 加密失效 | 使用 TLS,对密码进行哈希处理(bcrypt),加密 PII |
| 3 | 注入 | 使用参数化查询,验证输入 |
| 4 | 不安全设计 | 威胁建模,制定安全要求 |
| 5 | 安全配置错误 | 使用安全默认设置,移除未使用的功能 |
| 6 | 易受攻击的组件 | 保持依赖项更新,使用 Dependabot |
| 7 | 身份验证与会话问题 | 使用强密码、MFA,安全管理会话 |
| 8 | 软件与数据完整性 | 验证依赖项,为发布版本签名 |
| 9 | 日志记录与监控失效 | 记录安全事件,设置警报 |
| 10 | SSRF | 验证 URL,将允许的域名加入白名单 |
安全检查清单(部署前)
身份验证
- 使用 bcrypt 对密码进行哈希处理(12 轮以上)
- JWT 令牌使用 RS256,且有效期较短
- 会话 Cookie 设置 HttpOnly、Secure、SameSite
- 对登录端点实施速率限制(15 分钟内最多尝试 5 次)
- 密码重置使用安全令牌(1 小时后过期)
授权
- 所有路由都检查身份验证
- 在服务端验证权限
- 默认拒绝(白名单方式)
- 未重新进行身份验证时,不允许执行敏感操作
输入验证
- 使用架构(Zod)验证所有用户输入
- SQL 查询使用参数化查询/ORM
- 文件上传限制允许的类型和大小
- 根据上下文对输出进行转义
配置
- 代码或版本控制中不包含任何密钥
- 所有配置都使用环境变量
- 设置安全标头(CSP、HSTS、X-Frame-Options)
- 严格配置 CORS
- 在生产环境中强制使用 HTTPS
数据保护
- 对静态存储的 PII 进行加密
- 所有连接都使用 TLS/HTTPS
- 对数据库备份进行加密
- 安全管理密钥(Vault、AWS Secrets)
监控
- 对安全事件进行审计日志记录
- 配置错误跟踪(Sentry)
- 对所有公共端点实施速率限制
- 为登录失败尝试和 5xx 错误设置警报
依赖项
- 所有依赖项均为最新版本
- 已启用 Dependabot
- 不存在已知漏洞(运行
npm audit) - 尽量减少依赖项(移除未使用的依赖项)
常见安全错误
错误 1:客户端授权
❌ 错误:
// Hiding UI doesn't prevent access!
{
user.role === 'admin' && <DeleteButton />
}
✅ 正确:
// Always verify on server
app.delete('/api/users/:id', requireAdmin, deleteUser)
错误 2:信任客户端数据
❌ 错误:
// User can manipulate userId in request!
const userId = req.body.userId
await db.order.create({ data: { userId } })
✅ 正确:
// Get userId from authenticated session
const userId = req.user.id // From JWT/session
await db.order.create({ data: { userId } })
错误 3:密码要求过弱
❌ 错误:
if (password.length < 6) throw new Error('Too short')
✅ 正确:
const passwordSchema = z
.string()
.min(8, 'At least 8 characters')
.regex(/[A-Z]/, 'Needs uppercase')
.regex(/[a-z]/, 'Needs lowercase')
.regex(/[0-9]/, 'Needs number')
错误 4:不安全的直接对象引用
❌ 错误:
// User can access any document by changing ID!
const doc = await db.document.findUnique({ where: { id: req.params.id } })
return res.json(doc)
✅ 正确:
// Verify ownership
const doc = await db.document.findFirst({
where: {
id: req.params.id,
userId: req.user.id // Ensure user owns this document
}
})
if (!doc) return res.status(404).json({ error: 'Not found' })
return res.json(doc)
特定框架指南
Next.js 安全
// middleware.ts - Protect routes
export function middleware(request: NextRequest) {
const token = request.cookies.get('token')
// Redirect to login if no token
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/dashboard/:path*', '/api/private/:path*']
}
Express 安全
import helmet from 'helmet'
import mongoSanitize from 'express-mongo-sanitize'
app.use(helmet()) // Security headers
app.use(express.json({ limit: '10mb' })) // Prevent large payloads
app.use(mongoSanitize()) // Prevent NoSQL injection
FastAPI 安全
from fastapi import Depends, HTTPException
from fastapi.security import HTTPBearer
security = HTTPBearer()
async def get_current_user(token: str = Depends(security)):
try:
payload = verify_token(token.credentials)
return payload['user_id']
except:
raise HTTPException(status_code=401, detail="Invalid token")
@app.get("/protected")
async def protected_route(user_id: str = Depends(get_current_user)):
return {"user_id": user_id}
安全测试
自动化工具
- OWASP ZAP: Web 漏洞扫描器
- Snyk: 依赖项漏洞扫描
- npm audit: 检查已知漏洞
- Lighthouse: 在 Chrome DevTools 中进行安全审计
手动测试
- 尝试 SQL 注入:
' OR '1'='1 - 尝试 XSS:
<script>alert('XSS')</script> - 尝试 CSRF: 从不同源提交表单
- 尝试路径遍历:
../../etc/passwd - 尝试绕过身份验证: 不带令牌访问管理路由
何时使用此技能
在以下情况下使用 security-engineer 技能:
- ✅ 实现身份验证/授权
- ✅ 构建 API 端点
- ✅ 处理敏感数据(PII、支付、健康信息)
- ✅ 审查代码中的漏洞
- ✅ 部署到生产环境
- ✅ 开展安全审计
相关资源
技能:
api-designer- API 设计模式(与安全配合使用)testing-strategist- 安全测试策略deployment-advisor- 生产环境安全配置
模式:
/STANDARDS/architecture-patterns/authentication-patterns.md/STANDARDS/best-practices/security-best-practices.md
外部资源:
安全是每个人的责任。从第一天起就将其融入其中。 🔒
评论
支持 Markdown