mcpField
A
auth-security-reviewer

auth-security-reviewer

github.com/patricio0312rev安全63342 次安装41 周前更新
代码仓库

关于 auth-security-reviewer

auth-security-reviewer 面向开发者,用于审查身份验证系统。它通过清单和示例覆盖常见实现风险与安全测试。

主要功能

  1. 01检查会话与 Cookie 设置
  2. 02评估 JWT 过期和密钥处理
  3. 03检查 CSRF 与密码保护
  4. 04审查 MFA 和授权检查
  5. 05检查速率限制与安全测试

适用场景

  1. 01发布前审计会话设置
  2. 02审查基于 JWT 的登录实现
  3. 03检查 API 端点的授权

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/patricio0312rev/skills --skill auth-security-reviewer

安装后在 Claude Code 中输入/auth-security-reviewer确认已加载。

详细介绍

身份验证安全审查员

全面审查身份验证系统的安全性。

会话安全检查清单

// ❌ 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,
    }),
  })
);

JWT 安全审查

// ❌ 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);

CSRF 防护

// 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: "[email protected]", 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: "[email protected]",
        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);
    });
  });
});

最佳实践

  1. 重新生成会话:登录和权限变更时
  2. 短期令牌:访问令牌有效期为 15 分钟,刷新令牌有效期为 7 天
  3. CSRF 防护:应用于所有状态变更操作
  4. 速率限制:防止暴力破解
  5. 安全 Cookie:HttpOnly、Secure、SameSite
  6. MFA:用于敏感操作
  7. 审计日志:跟踪身份验证事件

输出检查清单

  • 已审查会话配置
  • 已保护 JWT 实现
  • 已启用 CSRF 防护
  • 使用 bcrypt 进行密码哈希
  • 已实现 MFA(如有需要)
  • 所有端点均已进行授权检查
  • 已防止会话固定攻击
  • 已对身份验证端点实施速率限制
  • 已编写安全测试
  • 已配置审计日志

评论

支持 Markdown