Fix Shadow API Exposure in Next.js
Shadow APIs in Next.js occur when developers deploy undocumented endpoints or leave 'internal' routes unprotected, assuming obscurity equals security. In Next.js, every file under 'pages/api' or 'app/api' is a public entry point. If you aren't explicitly guarding it, you're leaking it. Hackers use automated fuzzing to discover these orphaned routes and exploit the lack of authentication to dump databases or bypass business logic.
The Vulnerable Pattern
// pages/api/internal/debug-user.js // VULNERABILITY: No authentication, excessive data exposure, and insecure HTTP method handling. import db from '@/lib/db';
export default async function handler(req, res) { const { userId } = req.query; // Direct DB query based on URL params without session validation const user = await db.user.findUnique({ where: { id: userId } }); // Returns the entire user object including hashed passwords and internal flags res.status(200).json(user); }
The Secure Implementation
To kill Shadow API exposure, you must implement a Zero Trust architecture for your routes. First, enforce strict HTTP methods to prevent cross-site request forgery or unexpected behavior. Second, use centralized authentication (like Next-Auth) to ensure every request has a valid session. Third, leverage Zod for schema validation to prevent malformed inputs from reaching your data layer. Finally, use 'Data Minimization'—never return 'SELECT *' or full objects; explicitly select the fields your UI needs to prevent PII leakage.
// pages/api/internal/debug-user.js import { getServerSession } from 'next-auth/next'; import { authOptions } from '../auth/[...nextauth]'; import { z } from 'zod'; import db from '@/lib/db';const schema = z.object({ userId: z.string().uuid() });
export default async function handler(req, res) { // 1. Force Method Restriction if (req.method !== ‘GET’) return res.status(405).end();
// 2. Identity & Role Verification const session = await getServerSession(req, res, authOptions); if (!session || session.user.role !== ‘ADMIN’) { return res.status(403).json({ error: ‘Unauthorized access’ }); }
// 3. Input Validation const validated = schema.safeParse(req.query); if (!validated.success) return res.status(400).json(validated.error);
// 4. Data Minimization const user = await db.user.findUnique({ where: { id: validated.data.userId }, select: { id: true, email: true, lastLogin: true } // Only expose what is necessary });
return res.status(200).json(user); }
Your Next.js API
might be exposed to Shadow API Exposure
74% of Next.js apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.
Free Tier • No Credit Card • Instant Report
Verified by Ghost Labs Security Team
This content is continuously validated by our automated security engine and reviewed by our research team. Ghost Labs analyzes over 500+ vulnerability patterns across 40+ frameworks to provide up-to-date remediation strategies.