重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
auth-security-reviewer by patricio0312rev/skills
npx skills add https://github.com/patricio0312rev/skills --skill auth-security-reviewer对身份验证系统进行全面的安全审查。
// ❌ 不安全的会话配置
app.use(
session({
secret: "weak-secret", // 过于简单
resave: true, // 不必要
saveUninitialized: true, // 创建不必要的会话
cookie: {
secure: false, // 非HTTPS专用
httpOnly: false, // 可通过JavaScript访问
sameSite: false, // 存在CSRF漏洞
maxAge: 365 * 24 * 60 * 60 * 1000, // 1年 - 时间过长
},
})
);
// ✅ 安全的会话配置
app.use(
session({
secret: process.env.SESSION_SECRET, // 从环境变量获取
resave: false,
saveUninitialized: false,
name: "sessionId", // 不要使用默认的'connect.sid'
cookie: {
secure: true, // 仅限HTTPS
httpOnly: true, // 禁止JavaScript访问
sameSite: "strict", // CSRF防护
maxAge: 24 * 60 * 60 * 1000, // 24小时
domain: process.env.COOKIE_DOMAIN,
},
store: new RedisStore({
client: redisClient,
ttl: 86400,
}),
})
);
// ❌ 不安全的JWT实现
const token = jwt.sign(
{ userId: user.id },
"weak-secret", // 硬编码密钥
{ algorithm: "HS256" } // 无过期时间
);
// 存储在localStorage中
localStorage.setItem("token", token); // 存在XSS漏洞
// ✅ 安全的JWT实现
const token = jwt.sign(
{
userId: user.id,
role: user.role,
iat: Math.floor(Date.now() / 1000),
},
process.env.JWT_SECRET, // 从环境变量获取强密钥
{
algorithm: "HS256",
expiresIn: "15m", // 短期有效
issuer: "myapp.com",
audience: "myapp.com",
}
);
// 存储在httpOnly cookie中
res.cookie("accessToken", token, {
httpOnly: true,
secure: true,
sameSite: "strict",
maxAge: 15 * 60 * 1000,
});
// 刷新令牌,有效期较长
const refreshToken = jwt.sign(
{ userId: user.id, type: "refresh" },
process.env.REFRESH_TOKEN_SECRET,
{ expiresIn: "7d" }
);
// 将刷新令牌存储在数据库中
await storeRefreshToken(user.id, refreshToken);
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
// 使用csurf中间件
import csrf from "csurf";
const csrfProtection = csrf({ cookie: true });
// 应用于状态变更的路由
app.post("/api/transfer", csrfProtection, async (req, res) => {
// 受CSRF保护
await processTransfer(req.body);
res.json({ success: true });
});
// 向前端提供CSRF令牌
app.get("/api/csrf-token", csrfProtection, (req, res) => {
res.json({ csrfToken: req.csrfToken() });
});
// 前端使用
const csrfToken = await fetch("/api/csrf-token").then((r) => r.json());
await fetch("/api/transfer", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": csrfToken.csrfToken,
},
body: JSON.stringify({ amount: 100 }),
});
// ❌ 不安全的密码处理
const password = req.body.password;
const hash = crypto.createHash("md5").update(password).digest("hex"); // MD5已被破解
await db.user.create({ password: hash });
// ✅ 安全的密码处理
import bcrypt from "bcrypt";
// 哈希处理
const saltRounds = 12; // 根据安全要求调整
const hash = await bcrypt.hash(password, saltRounds);
await db.user.create({ passwordHash: hash });
// 验证
const isValid = await bcrypt.compare(password, user.passwordHash);
// 密码要求
function validatePassword(password: string): boolean {
return (
password.length >= 12 &&
/[A-Z]/.test(password) && // 大写字母
/[a-z]/.test(password) && // 小写字母
/[0-9]/.test(password) && // 数字
/[^A-Za-z0-9]/.test(password) // 特殊字符
);
}
// 检查是否在泄露密码中
import { pwnedPassword } from "hibp";
const breachCount = await pwnedPassword(password);
if (breachCount > 0) {
throw new Error("此密码已在数据泄露中被发现");
}
// 基于TOTP的MFA
import speakeasy from "speakeasy";
import qrcode from "qrcode";
// 生成密钥
const secret = speakeasy.generateSecret({
name: `MyApp (${user.email})`,
issuer: "MyApp",
});
// 存储密钥
await db.user.update({
where: { id: user.id },
data: {
mfaSecret: secret.base32,
mfaEnabled: false, // 验证前不启用
},
});
// 生成二维码
const qrCodeUrl = await qrcode.toDataURL(secret.otpauth_url);
// 验证TOTP令牌
function verifyMFA(token: string, secret: string): boolean {
return speakeasy.totp.verify({
secret,
encoding: "base32",
token,
window: 2, // 允许前后2个时间步长
});
}
// 备用代码
function generateBackupCodes(): string[] {
return Array.from({ length: 10 }, () =>
crypto.randomBytes(4).toString("hex").toUpperCase()
);
}
// ❌ 不安全:缺少授权检查
app.get("/api/users/:id/profile", async (req, res) => {
const profile = await db.user.findUnique({
where: { id: req.params.id },
});
res.json(profile); // 任何人都可以访问任何个人资料!
});
// ✅ 安全:正确的授权
app.get("/api/users/:id/profile", authenticate, async (req, res) => {
// 检查用户是否可以访问此个人资料
if (req.user.id !== req.params.id && req.user.role !== "ADMIN") {
return res.status(403).json({ error: "禁止访问" });
}
const profile = await db.user.findUnique({
where: { id: req.params.id },
});
res.json(profile);
});
// ❌ 不安全:IDOR漏洞
app.delete("/api/orders/:id", async (req, res) => {
await db.order.delete({ where: { id: req.params.id } });
res.json({ success: true });
});
// ✅ 安全:验证所有权
app.delete("/api/orders/:id", authenticate, async (req, res) => {
const order = await db.order.findUnique({
where: { id: req.params.id },
});
if (!order) {
return res.status(404).json({ error: "未找到" });
}
if (order.userId !== req.user.id) {
return res.status(403).json({ error: "禁止访问" });
}
await db.order.delete({ where: { id: req.params.id } });
res.json({ success: true });
});
// ❌ 不安全:登录时未重新生成会话
app.post("/login", async (req, res) => {
const user = await authenticate(req.body);
req.session.userId = user.id;
res.json({ success: true });
});
// ✅ 安全:登录时重新生成会话
app.post("/login", async (req, res) => {
const user = await authenticate(req.body);
// 重新生成会话以防止固定攻击
req.session.regenerate((err) => {
if (err) return res.status(500).json({ error: "服务器错误" });
req.session.userId = user.id;
res.json({ success: true });
});
});
// 权限提升时也重新生成
app.post("/admin/elevate", async (req, res) => {
// 验证管理员凭据
await verifyAdminPassword(req.body.password);
// 重新生成会话
req.session.regenerate((err) => {
if (err) return res.status(500).json({ error: "服务器错误" });
req.session.isAdmin = true;
res.json({ success: true });
});
});
import rateLimit from "express-rate-limit";
// 登录的严格速率限制
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 5, // 5次尝试
message: "登录尝试次数过多,请稍后再试",
standardHeaders: true,
legacyHeaders: false,
// 使用IP + 用户名进行更细粒度的限制
keyGenerator: (req) => `${req.ip}-${req.body.email}`,
});
app.post("/api/login", loginLimiter, async (req, res) => {
// 登录逻辑
});
// 密码重置的更严格限制
const resetLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1小时
max: 3,
message: "密码重置尝试次数过多",
});
app.post("/api/password-reset", resetLimiter, async (req, res) => {
// 密码重置逻辑
});
// tests/auth-security.test.ts
describe("身份验证安全", () => {
describe("会话安全", () => {
it("应设置httpOnly cookie", async () => {
const response = await request(app)
.post("/api/login")
.send({ email: "test@example.com", password: "password123" });
const cookie = response.headers["set-cookie"][0];
expect(cookie).toContain("HttpOnly");
expect(cookie).toContain("Secure");
expect(cookie).toContain("SameSite=Strict");
});
it("应在登录时重新生成会话", async () => {
const agent = request.agent(app);
// 获取初始会话
await agent.get("/");
const initialCookie = agent.jar.getCookie("sessionId");
// 登录
await agent.post("/api/login").send({
email: "test@example.com",
password: "password123",
});
const loginCookie = agent.jar.getCookie("sessionId");
// 会话ID应更改
expect(loginCookie.value).not.toBe(initialCookie.value);
});
});
describe("CSRF防护", () => {
it("应拒绝没有CSRF令牌的请求", async () => {
await request(app)
.post("/api/transfer")
.send({ amount: 100 })
.expect(403);
});
it("应接受带有有效CSRF令牌的请求", async () => {
const { csrfToken } = await request(app)
.get("/api/csrf-token")
.then((r) => r.body);
await request(app)
.post("/api/transfer")
.set("X-CSRF-Token", csrfToken)
.send({ amount: 100 })
.expect(200);
});
});
describe("授权", () => {
it("应防止IDOR攻击", async () => {
const user1 = await createUser();
const user2 = await createUser();
const token1 = generateToken(user1);
// 尝试使用user1的令牌访问user2的个人资料
await request(app)
.get(`/api/users/${user2.id}/profile`)
.set("Authorization", `Bearer ${token1}`)
.expect(403);
});
});
});
每周安装量
65
代码仓库
GitHub星标数
21
首次出现时间
2026年1月24日
安全审计
安装于
opencode55
codex54
claude-code53
gemini-cli53
github-copilot50
cursor48
Comprehensive security review of authentication systems.
// ❌ INSECURE Session Configuration
app.use(
session({
secret: "weak-secret", // Too simple
resave: true, // Unnecessary
saveUninitialized: true, // Creates unnecessary sessions
cookie: {
secure: false, // Not HTTPS-only
httpOnly: false, // Accessible via JavaScript
sameSite: false, // CSRF vulnerable
maxAge: 365 * 24 * 60 * 60 * 1000, // 1 year - too long
},
})
);
// ✅ SECURE Session Configuration
app.use(
session({
secret: process.env.SESSION_SECRET, // From environment
resave: false,
saveUninitialized: false,
name: "sessionId", // Don't use default 'connect.sid'
cookie: {
secure: true, // HTTPS only
httpOnly: true, // No JavaScript access
sameSite: "strict", // CSRF protection
maxAge: 24 * 60 * 60 * 1000, // 24 hours
domain: process.env.COOKIE_DOMAIN,
},
store: new RedisStore({
client: redisClient,
ttl: 86400,
}),
})
);
// ❌ INSECURE JWT Implementation
const token = jwt.sign(
{ userId: user.id },
"weak-secret", // Hardcoded secret
{ algorithm: "HS256" } // No expiration
);
// Store in localStorage
localStorage.setItem("token", token); // XSS vulnerable
// ✅ SECURE JWT Implementation
const token = jwt.sign(
{
userId: user.id,
role: user.role,
iat: Math.floor(Date.now() / 1000),
},
process.env.JWT_SECRET, // Strong secret from env
{
algorithm: "HS256",
expiresIn: "15m", // Short-lived
issuer: "myapp.com",
audience: "myapp.com",
}
);
// Store in httpOnly cookie
res.cookie("accessToken", token, {
httpOnly: true,
secure: true,
sameSite: "strict",
maxAge: 15 * 60 * 1000,
});
// Refresh token with longer expiry
const refreshToken = jwt.sign(
{ userId: user.id, type: "refresh" },
process.env.REFRESH_TOKEN_SECRET,
{ expiresIn: "7d" }
);
// Store refresh token in database
await storeRefreshToken(user.id, refreshToken);
// Using csurf middleware
import csrf from "csurf";
const csrfProtection = csrf({ cookie: true });
// Apply to state-changing routes
app.post("/api/transfer", csrfProtection, async (req, res) => {
// Protected from CSRF
await processTransfer(req.body);
res.json({ success: true });
});
// Provide CSRF token to frontend
app.get("/api/csrf-token", csrfProtection, (req, res) => {
res.json({ csrfToken: req.csrfToken() });
});
// Frontend usage
const csrfToken = await fetch("/api/csrf-token").then((r) => r.json());
await fetch("/api/transfer", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": csrfToken.csrfToken,
},
body: JSON.stringify({ amount: 100 }),
});
// ❌ INSECURE Password Handling
const password = req.body.password;
const hash = crypto.createHash("md5").update(password).digest("hex"); // MD5 is broken
await db.user.create({ password: hash });
// ✅ SECURE Password Handling
import bcrypt from "bcrypt";
// Hashing
const saltRounds = 12; // Adjust based on security requirements
const hash = await bcrypt.hash(password, saltRounds);
await db.user.create({ passwordHash: hash });
// Verification
const isValid = await bcrypt.compare(password, user.passwordHash);
// Password requirements
function validatePassword(password: string): boolean {
return (
password.length >= 12 &&
/[A-Z]/.test(password) && // Uppercase
/[a-z]/.test(password) && // Lowercase
/[0-9]/.test(password) && // Number
/[^A-Za-z0-9]/.test(password) // Special char
);
}
// Check against breached passwords
import { pwnedPassword } from "hibp";
const breachCount = await pwnedPassword(password);
if (breachCount > 0) {
throw new Error("This password has been found in data breaches");
}
// TOTP-based MFA
import speakeasy from "speakeasy";
import qrcode from "qrcode";
// Generate secret
const secret = speakeasy.generateSecret({
name: `MyApp (${user.email})`,
issuer: "MyApp",
});
// Store secret
await db.user.update({
where: { id: user.id },
data: {
mfaSecret: secret.base32,
mfaEnabled: false, // Not enabled until verified
},
});
// Generate QR code
const qrCodeUrl = await qrcode.toDataURL(secret.otpauth_url);
// Verify TOTP token
function verifyMFA(token: string, secret: string): boolean {
return speakeasy.totp.verify({
secret,
encoding: "base32",
token,
window: 2, // Allow 2 time steps before/after
});
}
// Backup codes
function generateBackupCodes(): string[] {
return Array.from({ length: 10 }, () =>
crypto.randomBytes(4).toString("hex").toUpperCase()
);
}
// ❌ INSECURE: Missing authorization check
app.get("/api/users/:id/profile", async (req, res) => {
const profile = await db.user.findUnique({
where: { id: req.params.id },
});
res.json(profile); // Anyone can access any profile!
});
// ✅ SECURE: Proper authorization
app.get("/api/users/:id/profile", authenticate, async (req, res) => {
// Check if user can access this profile
if (req.user.id !== req.params.id && req.user.role !== "ADMIN") {
return res.status(403).json({ error: "Forbidden" });
}
const profile = await db.user.findUnique({
where: { id: req.params.id },
});
res.json(profile);
});
// ❌ INSECURE: IDOR vulnerability
app.delete("/api/orders/:id", async (req, res) => {
await db.order.delete({ where: { id: req.params.id } });
res.json({ success: true });
});
// ✅ SECURE: Verify ownership
app.delete("/api/orders/:id", authenticate, async (req, res) => {
const order = await db.order.findUnique({
where: { id: req.params.id },
});
if (!order) {
return res.status(404).json({ error: "Not found" });
}
if (order.userId !== req.user.id) {
return res.status(403).json({ error: "Forbidden" });
}
await db.order.delete({ where: { id: req.params.id } });
res.json({ success: true });
});
// ❌ INSECURE: Session not regenerated on login
app.post("/login", async (req, res) => {
const user = await authenticate(req.body);
req.session.userId = user.id;
res.json({ success: true });
});
// ✅ SECURE: Regenerate session on login
app.post("/login", async (req, res) => {
const user = await authenticate(req.body);
// Regenerate session to prevent fixation
req.session.regenerate((err) => {
if (err) return res.status(500).json({ error: "Server error" });
req.session.userId = user.id;
res.json({ success: true });
});
});
// Also regenerate on privilege escalation
app.post("/admin/elevate", async (req, res) => {
// Verify admin credentials
await verifyAdminPassword(req.body.password);
// Regenerate session
req.session.regenerate((err) => {
if (err) return res.status(500).json({ error: "Server error" });
req.session.isAdmin = true;
res.json({ success: true });
});
});
import rateLimit from "express-rate-limit";
// Strict rate limit for login
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 attempts
message: "Too many login attempts, please try again later",
standardHeaders: true,
legacyHeaders: false,
// Use IP + username for more granular limiting
keyGenerator: (req) => `${req.ip}-${req.body.email}`,
});
app.post("/api/login", loginLimiter, async (req, res) => {
// Login logic
});
// Even stricter for password reset
const resetLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 3,
message: "Too many password reset attempts",
});
app.post("/api/password-reset", resetLimiter, async (req, res) => {
// Password reset logic
});
// tests/auth-security.test.ts
describe("Auth Security", () => {
describe("Session Security", () => {
it("should set httpOnly cookie", async () => {
const response = await request(app)
.post("/api/login")
.send({ email: "test@example.com", password: "password123" });
const cookie = response.headers["set-cookie"][0];
expect(cookie).toContain("HttpOnly");
expect(cookie).toContain("Secure");
expect(cookie).toContain("SameSite=Strict");
});
it("should regenerate session on login", async () => {
const agent = request.agent(app);
// Get initial session
await agent.get("/");
const initialCookie = agent.jar.getCookie("sessionId");
// Login
await agent.post("/api/login").send({
email: "test@example.com",
password: "password123",
});
const loginCookie = agent.jar.getCookie("sessionId");
// Session ID should change
expect(loginCookie.value).not.toBe(initialCookie.value);
});
});
describe("CSRF Protection", () => {
it("should reject requests without CSRF token", async () => {
await request(app)
.post("/api/transfer")
.send({ amount: 100 })
.expect(403);
});
it("should accept requests with valid CSRF token", async () => {
const { csrfToken } = await request(app)
.get("/api/csrf-token")
.then((r) => r.body);
await request(app)
.post("/api/transfer")
.set("X-CSRF-Token", csrfToken)
.send({ amount: 100 })
.expect(200);
});
});
describe("Authorization", () => {
it("should prevent IDOR attacks", async () => {
const user1 = await createUser();
const user2 = await createUser();
const token1 = generateToken(user1);
// Try to access user2's profile with user1's token
await request(app)
.get(`/api/users/${user2.id}/profile`)
.set("Authorization", `Bearer ${token1}`)
.expect(403);
});
});
});
Weekly Installs
65
Repository
GitHub Stars
21
First Seen
Jan 24, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode55
codex54
claude-code53
gemini-cli53
github-copilot50
cursor48
浏览器自动化策略指南:何时及如何使用实时浏览器会话进行网页调试与研究
45,600 周安装
React Native 移动端 UI 设计规范与无障碍开发指南 | 最佳实践
524 周安装
产品管理技能包 (2026版) - 含PRD/路线图/OKR模板、决策树与最佳实践
536 周安装
llama.cpp:纯C/C++大语言模型推理引擎,CPU/非NVIDIA硬件优化,边缘部署
62 周安装
代码安全指南:15+语言安全编码规则,覆盖OWASP Top 10与基础设施安全
535 周安装
线框原型设计指南:从低保真到高保真,提升产品设计效率与用户体验
525 周安装
Vercel React 最佳实践指南:45条性能优化规则与Next.js应用优化
526 周安装