Install
openclaw skills install sec-audit-cn在中国等地区进行代码安全审计、安全编码与评审时使用:覆盖 OWASP Top 10、鉴权与授权、密钥与配置、CORS/CSP、 输入校验与防注入、XSS/CSRF、依赖漏洞、日志与错误处理;输出分级结论与可执行修复建议。 适用于 Web/API、移动端后端、小程序服务端、涉及个人信息与支付回调的业务。
openclaw skills install sec-audit-cn面向中国等地区业务场景的应用安全审计与安全编码指引。技术框架与 OWASP 分类与国际实践一致;在数据、身份与第三方集成上补充国内常见风险点(下文「国内场景提示」)。改编自 buildwithclaude(Dave Poon, MIT)并做本地化与结构优化。
非法律意见:涉及《网络安全法》《数据安全法》《个人信息保护法》等合规边界时,仅作开发侧注意项列举,重大事项应咨询法务与合规。
你是资深应用安全工程师,擅长安全编码、漏洞识别与 OWASP 对齐的评审。你产出可落地的修复与优先级,避免空泛理论。
// ❌ BAD: No authorization check
app.delete('/api/posts/:id', async (req, res) => {
await db.post.delete({ where: { id: req.params.id } })
res.json({ success: true })
})
// ✅ GOOD: Verify ownership
app.delete('/api/posts/:id', authenticate, async (req, res) => {
const post = await db.post.findUnique({ where: { id: req.params.id } })
if (!post) return res.status(404).json({ error: 'Not found' })
if (post.authorId !== req.user.id && req.user.role !== 'admin') {
return res.status(403).json({ error: 'Forbidden' })
}
await db.post.delete({ where: { id: req.params.id } })
res.json({ success: true })
})
检查项:
* 搭配凭据;白名单明确。// ❌ BAD: Storing plaintext passwords
await db.user.create({ data: { password: req.body.password } })
// ✅ GOOD: Bcrypt with sufficient rounds
import bcrypt from 'bcryptjs'
const hashedPassword = await bcrypt.hash(req.body.password, 12)
await db.user.create({ data: { password: hashedPassword } })
检查项:
// ❌ BAD: SQL injection vulnerable
const query = `SELECT * FROM users WHERE email = '${email}'`
// ✅ GOOD: Parameterized queries
const user = await db.query('SELECT * FROM users WHERE email = $1', [email])
// ✅ GOOD: ORM with parameterized input
const user = await prisma.user.findUnique({ where: { email } })
// ❌ BAD: Command injection
const result = exec(`ls ${userInput}`)
// ✅ GOOD: Use execFile with argument array
import { execFile } from 'child_process'
execFile('ls', [sanitizedPath], callback)
检查项:
eval、Function、动态模板执行代码。XSS / 输出编码(常归入注入与输出处理):
// ❌ BAD: dangerouslySetInnerHTML with user input
<div dangerouslySetInnerHTML={{ __html: userComment }} />
// ✅ GOOD: Sanitize HTML
import DOMPurify from 'isomorphic-dompurify'
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userComment) }} />
// ✅ BEST: Render as text (React auto-escapes)
<div>{userComment}</div>
检查项:
检查项:
检查项:
npm audit / pnpm audit / pip-audit / mvn dependency-check 等按栈选用)。检查项:
检查项:
检查项:
curl | bash。检查项:
检查项:
// next.config.js
const securityHeaders = [
{ key: 'X-DNS-Prefetch-Control', value: 'on' },
{ key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
{ key: 'X-Frame-Options', value: 'SAMEORIGIN' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
{
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",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'",
].join('; '),
},
]
module.exports = {
async headers() {
return [{ source: '/(.*)', headers: securityHeaders }]
},
}
import { z } from 'zod'
const userSchema = z.object({
email: z.string().email().max(255),
password: z.string().min(8).max(128),
name: z.string().min(1).max(100).regex(/^[a-zA-Z\s'-]+$/),
age: z.number().int().min(13).max(150).optional(),
})
export async function createUser(formData: FormData) {
'use server'
const parsed = userSchema.safeParse({
email: formData.get('email'),
password: formData.get('password'),
name: formData.get('name'),
})
if (!parsed.success) {
return { error: parsed.error.flatten() }
}
}
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp']
const MAX_SIZE = 5 * 1024 * 1024 // 5MB
export async function uploadFile(formData: FormData) {
'use server'
const file = formData.get('file') as File
if (!file || file.size === 0) return { error: 'No file' }
if (!ALLOWED_TYPES.includes(file.type)) return { error: 'Invalid file type' }
if (file.size > MAX_SIZE) return { error: 'File too large' }
const bytes = new Uint8Array(await file.arrayBuffer())
if (!validateMagicBytes(bytes, file.type)) return { error: 'File content mismatch' }
}
import { SignJWT, jwtVerify } from 'jose'
const secret = new TextEncoder().encode(process.env.JWT_SECRET) // min 256-bit
export async function createToken(payload: { userId: string; role: string }) {
return new SignJWT(payload)
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('15m')
.setAudience('your-app')
.setIssuer('your-app')
.sign(secret)
}
export async function verifyToken(token: string) {
try {
const { payload } = await jwtVerify(token, secret, {
algorithms: ['HS256'],
audience: 'your-app',
issuer: 'your-app',
})
return payload
} catch {
return null
}
}
cookies().set('session', token, {
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 60 * 60 * 24 * 7,
path: '/',
})
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis'
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, '10 s'),
})
const ip = request.headers.get('x-forwarded-for') ?? '127.0.0.1'
const { success } = await ratelimit.limit(ip)
if (!success) {
return NextResponse.json({ error: 'Too many requests' }, { status: 429 })
}
// ❌ BAD
const API_KEY = 'sk-1234567890abcdef'
// ✅ GOOD
const API_KEY = process.env.API_KEY
if (!API_KEY) throw new Error('API_KEY not configured')
规则摘要: 勿提交 .env;多环境密钥隔离;生产用密钥管理服务;日志与错误中不出现密钥。
npm audit && npm audit fix
# 或
pnpm audit
pip-audit -r requirements.txt
mvn org.owasp:dependency-check-maven:check
session_key、access_token 仅服务端使用;勿下发给前端用于敏感操作。## 安全审计报告(sec-audit-cn)
### 严重(必须修复)
1. **[A03: 注入]** `/api/search` 存在 SQL 拼接 — 用户输入直接进入查询
- 文件:`app/api/search/route.ts:15`
- 修复:改为参数化查询或 ORM 安全 API
- 风险:数据库被拖库
### 高(应尽快修复)
1. **[A01: 访问控制]** DELETE 接口缺少鉴权与归属校验
- 文件:`app/api/posts/[id]/route.ts:42`
- 修复:中间件认证 + 资源归属判断
### 中(建议修复)
1. **[A05: 配置错误]** 缺少 CSP、HSTS 等安全头
- 修复:按框架配置统一响应头
### 低(酌情)
1. **[A06: 依赖]** 若干依赖存在已知 CVE
- 建议:执行 `npm audit fix` 并评估破坏性升级
.env*、密钥与 CI 密钥引用auth.ts、middleware.ts、**/api/auth/**prisma/schema.prisma 或等价数据模型(权限、RLS)next.config.* / 网关配置(安全头、重定向)package.json / 锁文件