mcpField
S
security-nextjs

security-nextjs

github.com/igorwarzocha安全122368 次安装38 周前更新
代码仓库

关于 security-nextjs

面向 Next.js 开发者的安全审计技能,涵盖环境变量、Server Actions、中间件、API 路由和 App Router,并提供搜索检查与修复模式。

主要功能

  1. 01检查环境变量是否泄露
  2. 02审查 Server Actions 的认证与校验
  3. 03审计 API 路由和中间件保护
  4. 04查找 IDOR 与不安全 HTML
  5. 05检查安全标头和配置

适用场景

  1. 01发布前审计 Next.js 应用
  2. 02查找客户端代码包中暴露的密钥
  3. 03审查路由与操作的授权
  4. 04检查中间件匹配器遗漏的路径

安装

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

$npx skills add https://github.com/igorwarzocha/opencode-workflows --skill security-nextjs

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

附带文件scripts/scan.sh

详细介绍

针对 Next.js 应用的安全审计模式,涵盖环境变量暴露、Server Actions、中间件身份验证、API 路由和 App Router 安全。

环境变量暴露

NEXT_PUBLIC_ 陷阱
NEXT_PUBLIC_* → Bundled into client JavaScript → Visible to everyone
No prefix     → Server-only → Safe for secrets

审计步骤:

  1. grep -r "NEXT_PUBLIC_" . -g "*.env*"
  2. 对每个变量都要问:「如果它出现在 view-source 中,我能接受吗?」
  3. 常见错误:
    • NEXT_PUBLIC_API_KEY(应该仅限服务器端使用)
    • NEXT_PUBLIC_DATABASE_URL(绝不能使用)
    • NEXT_PUBLIC_STRIPE_SECRET_KEY(应使用 STRIPE_SECRET_KEY)

安全模式:

// Server-only (API route, Server Component, Server Action)
const apiKey = process.env.API_KEY; // ✓ No NEXT_PUBLIC_

// Client-safe (truly public)
const publishableKey = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY; // ✓ Publishable
next.config.js 中的 env 始终会打包

在 next.config.js 的 env 下设置的值会内联到客户端包中,即使没有 NEXT_PUBLIC_ 前缀也是如此。应将它们视为公开信息。

// ❌ Sensitive values here are exposed to the browser
module.exports = {
  env: {
    DATABASE_URL: process.env.DATABASE_URL,
  },
};

Server Actions 安全

缺少身份验证(最常见问题)
// ❌ VULNERABLE: No auth check
"use server"
export async function deleteUser(userId: string) {
  await db.user.delete({ where: { id: userId } });
}

// ✓ SECURE: Auth + authorization
"use server"
export async function deleteUser(userId: string) {
  const session = await getServerSession();
  if (!session) throw new Error("Unauthorized");
  if (session.user.id !== userId && !session.user.isAdmin) {
    throw new Error("Forbidden");
  }
  await db.user.delete({ where: { id: userId } });
}
输入验证
// ❌ Trusts client input
"use server"
export async function updateProfile(data: any) {
  await db.user.update({ data });
}

// ✓ Validates with Zod
"use server"
import { z } from "zod";
const schema = z.object({ name: z.string().max(100), bio: z.string().max(500) });
export async function updateProfile(formData: FormData) {
  const data = schema.parse(Object.fromEntries(formData));
  await db.user.update({ data });
}

API 路由安全

App Router(app/api/*/route.ts)
// ❌ No auth
export async function GET(request: Request) {
  return Response.json(await db.users.findMany());
}

// ✓ Auth middleware
import { getServerSession } from "next-auth";
export async function GET(request: Request) {
  const session = await getServerSession();
  if (!session) return new Response("Unauthorized", { status: 401 });
  // ...
}
Pages Router(pages/api/*.ts)
// Check for missing auth on all handlers
// Common issue: GET is public but POST has auth (inconsistent)

中间件安全

middleware.ts 中的身份验证
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  const token = request.cookies.get("session");
  
  // ❌ Just checking existence
  if (!token) return NextResponse.redirect("/login");
  
  // ✓ SHOULD verify token
  // But middleware can't do async DB calls easily!
  // Solution: Use next-auth middleware or verify JWT
}

// CRITICAL: Check matcher covers all protected routes
export const config = {
  matcher: ["/dashboard/:path*", "/admin/:path*", "/api/admin/:path*"],
};
Matcher 遗漏
// ❌ Forgot API routes
matcher: ["/dashboard/:path*"]
// Admin API at /api/admin/* is unprotected!

// ✓ Include API routes
matcher: ["/dashboard/:path*", "/api/admin/:path*"]

标头与安全配置

next.config.js
// Check for security headers
module.exports = {
  async headers() {
    return [
      {
        source: "/:path*",
        headers: [
          { key: "X-Frame-Options", value: "DENY" },
          { key: "X-Content-Type-Options", value: "nosniff" },
          { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
          // CSP is complex - check if present and not too permissive
        ],
      },
    ];
  },
};

<severity_table>

常见漏洞

问题检查位置严重程度
NEXT_PUBLIC_ 密钥.env* 文件严重
未经身份验证的 Server Actionsapp/**/actions.ts高
未经身份验证的 API 路由app/api/**/route.ts、pages/api/**高
中间件 matcher 遗漏middleware.ts高
缺少输入验证Server Actions、API 路由高
动态路由中的 IDOR未进行所有权检查的 [id] 参数高
dangerouslySetInnerHTML组件中
缺少安全标头next.config.js低

</severity_table>

快速 Grep 命令

# Find NEXT_PUBLIC_ usage
grep -r "NEXT_PUBLIC_" . -g "*.env*" -g "*.ts" -g "*.tsx"

# Find next.config env usage (always bundled)
rg -n 'env\s*:' next.config.*

# Find Server Actions without auth
rg -l '"use server"' . | xargs rg -L '(getServerSession|auth\(|getSession|currentUser)'

# Find API routes
fd 'route\.(ts|js)' app/api/

# Find dangerouslySetInnerHTML
rg 'dangerouslySetInnerHTML' . -g "*.tsx" -g "*.jsx"

评论

支持 Markdown