security
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSecurity Skill
安全技能指南
Dependency Scanning
依赖项扫描
pnpm audit
pnpm 审计
bash
undefinedbash
undefinedRun audit
执行审计
pnpm audit
pnpm audit
Only high/critical
仅检查高/严重等级漏洞
pnpm audit --audit-level=high
pnpm audit --audit-level=high
Auto-fix
自动修复
pnpm audit --fix
pnpm audit --fix
JSON report
生成JSON报告
pnpm audit --json > audit.json
undefinedpnpm audit --json > audit.json
undefinedFix Vulnerabilities
修复漏洞
Direct dependencies: Update version in catalog
pnpm-workspace.yamlTransitive dependencies:
bash
undefined直接依赖项: 在的依赖清单中更新版本
pnpm-workspace.yaml间接依赖项:
bash
undefinedFind dependency chain
查找依赖链
pnpm why vulnerable-package
pnpm why vulnerable-package
Use overrides as last resort
仅在万不得已时使用覆盖配置
package.json
package.json
{
"pnpm": {
"overrides": {
"vulnerable-package": "^3.1.0"
}
}
}
undefined{
"pnpm": {
"overrides": {
"vulnerable-package": "^3.1.0"
}
}
}
undefinedSnyk
Snyk
bash
snyk auth # Authenticate
snyk test # Test for vulnerabilities
snyk monitor # Monitor for new vulnerabilities
snyk fix # Auto-fixbash
snyk auth # 身份验证
snyk test # 检测漏洞
snyk monitor # 持续监控新漏洞
snyk fix # 自动修复OWASP Top 10 Checks
OWASP Top 10 检查项
1. Broken Access Control
1. 访问控制失效
typescript
// ❌ No authorization
export async function deletePost(postId: string) {
await db.delete(posts).where(eq(posts.id, postId));
}
// ✅ With authorization
export async function deletePost(postId: string, userId: string) {
const post = await db.query.posts.findFirst({ where: eq(posts.id, postId) });
if (post.authorId !== userId) throw new Error("Unauthorized");
await db.delete(posts).where(eq(posts.id, postId));
}typescript
// ❌ 未添加授权校验
export async function deletePost(postId: string) {
await db.delete(posts).where(eq(posts.id, postId));
}
// ✅ 添加授权校验
export async function deletePost(postId: string, userId: string) {
const post = await db.query.posts.findFirst({ where: eq(posts.id, postId) });
if (post.authorId !== userId) throw new Error("Unauthorized");
await db.delete(posts).where(eq(posts.id, postId));
}2. Injection Prevention
2. 注入攻击防护
typescript
// ❌ SQL Injection
const query = `SELECT * FROM users WHERE id = ${userId}`;
// ✅ Parameterized query (Drizzle ORM)
const user = await db.query.users.findFirst({ where: eq(users.id, userId) });typescript
// ❌ 存在SQL注入风险
const query = `SELECT * FROM users WHERE id = ${userId}`;
// ✅ 使用参数化查询(Drizzle ORM)
const user = await db.query.users.findFirst({ where: eq(users.id, userId) });3. XSS Prevention
3. XSS攻击防护
React escapes content by default. When rendering HTML:
- Sanitize with library before rendering
sanitize-html - Never render untrusted content directly
React默认会转义内容。渲染HTML时:
- 渲染前使用库进行内容清理
sanitize-html - 绝不要直接渲染不可信内容
4. Rate Limiting
4. 限流控制
typescript
import { Ratelimit } from "@upstash/ratelimit";
import { redis } from "@sgcarstrends/utils";
const ratelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(5, "15 m"),
});
export async function login(email: string, password: string, ip: string) {
const { success } = await ratelimit.limit(ip);
if (!success) throw new Error("Too many login attempts");
return verifyCredentials(email, password);
}typescript
import { Ratelimit } from "@upstash/ratelimit";
import { redis } from "@sgcarstrends/utils";
const ratelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(5, "15 m"),
});
export async function login(email: string, password: string, ip: string) {
const { success } = await ratelimit.limit(ip);
if (!success) throw new Error("Too many login attempts");
return verifyCredentials(email, password);
}5. Password Security
5. 密码安全
typescript
import bcrypt from "bcrypt";
// ✅ Hash passwords
const hashedPassword = await bcrypt.hash(password, 10);
// ✅ Strong password validation
const passwordSchema = z.string()
.min(12)
.regex(/[A-Z]/, "Must contain uppercase")
.regex(/[a-z]/, "Must contain lowercase")
.regex(/[0-9]/, "Must contain number")
.regex(/[^A-Za-z0-9]/, "Must contain special character");typescript
import bcrypt from "bcrypt";
// ✅ 对密码进行哈希处理
const hashedPassword = await bcrypt.hash(password, 10);
// ✅ 强密码校验
const passwordSchema = z.string()
.min(12)
.regex(/[A-Z]/, "Must contain uppercase")
.regex(/[a-z]/, "Must contain lowercase")
.regex(/[0-9]/, "Must contain number")
.regex(/[^A-Za-z0-9]/, "Must contain special character");6. SSRF Prevention
6. SSRF攻击防护
typescript
// ❌ SSRF vulnerability
export async function fetchUrl(url: string) {
return await fetch(url);
}
// ✅ Whitelist approach
const ALLOWED_DOMAINS = ["api.example.com", "data.gov.sg"];
export async function fetchUrl(url: string) {
const parsedUrl = new URL(url);
if (!ALLOWED_DOMAINS.includes(parsedUrl.hostname)) {
throw new Error("Domain not allowed");
}
return await fetch(url);
}typescript
// ❌ 存在SSRF漏洞风险
export async function fetchUrl(url: string) {
return await fetch(url);
}
// ✅ 采用白名单机制
const ALLOWED_DOMAINS = ["api.example.com", "data.gov.sg"];
export async function fetchUrl(url: string) {
const parsedUrl = new URL(url);
if (!ALLOWED_DOMAINS.includes(parsedUrl.hostname)) {
throw new Error("Domain not allowed");
}
return await fetch(url);
}Input Validation
输入校验
typescript
import { z } from "zod";
const userInputSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().min(0).max(150),
});
export async function createUser(data: unknown) {
const validated = userInputSchema.parse(data);
// Now safe to use
}typescript
import { z } from "zod";
const userInputSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().min(0).max(150),
});
export async function createUser(data: unknown) {
const validated = userInputSchema.parse(data);
// 此时数据可安全使用
}CORS Configuration
CORS配置
typescript
// ❌ Too permissive
app.use(cors({ origin: "*" }));
// ✅ Whitelist specific origins
app.use(cors({
origin: [
"https://sgcarstrends.com",
"https://staging.sgcarstrends.com",
process.env.NODE_ENV === "development" ? "http://localhost:3001" : "",
].filter(Boolean),
credentials: true,
}));typescript
// ❌ 配置过于宽松
app.use(cors({ origin: "*" }));
// ✅ 配置特定域名白名单
app.use(cors({
origin: [
"https://sgcarstrends.com",
"https://staging.sgcarstrends.com",
process.env.NODE_ENV === "development" ? "http://localhost:3001" : "",
].filter(Boolean),
credentials: true,
}));Security Headers
安全响应头
typescript
// next.config.js
const securityHeaders = [
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },
{ key: "X-Frame-Options", value: "SAMEORIGIN" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "X-XSS-Protection", value: "1; mode=block" },
{ key: "Referrer-Policy", value: "origin-when-cross-origin" },
];
module.exports = {
async headers() {
return [{ source: "/:path*", headers: securityHeaders }];
},
};typescript
// next.config.js
const securityHeaders = [
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },
{ key: "X-Frame-Options", value: "SAMEORIGIN" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "X-XSS-Protection", value: "1; mode=block" },
{ key: "Referrer-Policy", value: "origin-when-cross-origin" },
];
module.exports = {
async headers() {
return [{ source: "/:path*", headers: securityHeaders }];
},
};Environment Variables
环境变量
typescript
// ❌ Hardcoded secret
const apiKey = "sk_live_EXAMPLE_NOT_REAL";
// ✅ From environment with validation
import { z } from "zod";
const envSchema = z.object({
API_KEY: z.string().min(1),
DATABASE_URL: z.string().url(),
});
const env = envSchema.parse(process.env);typescript
// ❌ 硬编码密钥
const apiKey = "sk_live_EXAMPLE_NOT_REAL";
// ✅ 从环境变量读取并校验
import { z } from "zod";
const envSchema = z.object({
API_KEY: z.string().min(1),
DATABASE_URL: z.string().url(),
});
const env = envSchema.parse(process.env);CI Integration
CI集成
yaml
undefinedyaml
undefined.github/workflows/security.yml
.github/workflows/security.yml
name: Security Audit
on:
push:
branches: [main]
schedule:
- cron: '0 0 * * 1' # Weekly
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- run: pnpm install
- run: pnpm audit --audit-level=high
undefinedname: Security Audit
on:
push:
branches: [main]
schedule:
- cron: '0 0 * * 1' # 每周执行
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
- run: pnpm install
- run: pnpm audit --audit-level=high
undefinedSecurity Checklist
安全检查清单
- All user input validated (Zod schemas)
- SQL injection prevented (using ORM)
- XSS prevented (React escaping, sanitization)
- Authentication implemented correctly
- Authorization checks in place
- Passwords hashed (bcrypt/argon2)
- Rate limiting configured
- Security headers set
- CORS configured properly
- HTTPS enforced
- Dependencies audited (pnpm audit)
- Secrets in environment variables
- Error messages don't leak info
- 所有用户输入已通过Zod Schema校验
- 已通过ORM防止SQL注入
- 已通过React转义及内容清理防止XSS攻击
- 已正确实现身份认证机制
- 已添加访问授权校验
- 密码已通过bcrypt/argon2进行哈希处理
- 已配置限流控制
- 已设置安全响应头
- CORS已正确配置
- 已强制使用HTTPS
- 已通过pnpm audit审计依赖项
- 密钥已存储在环境变量中
- 错误信息未泄露敏感数据
References
参考资料
- OWASP Top 10: https://owasp.org/www-project-top-ten
- pnpm Audit: https://pnpm.io/cli/audit
- Snyk: https://snyk.io
- OWASP Top 10:https://owasp.org/www-project-top-ten
- pnpm 审计:https://pnpm.io/cli/audit
- Snyk:https://snyk.io