Fix Shadow API Exposure in Nitro
Nitro's file-system routing is a double-edged sword: ease of development often leads to 'Shadow APIs'—undocumented, unauthenticated endpoints lurking in the `server/api` directory. In a Nitro environment, if a file exists in the api path, it is public. Attackers crawl these for 'debug', 'test', or 'internal' scripts left behind by developers. To secure Nitro, you must enforce strict routing patterns, move internal logic to non-routed directories, and implement global middleware guards.
The Vulnerable Pattern
// server/api/internal/test-db-connection.ts
// VULNERABILITY: This file is automatically exposed as /api/internal/test-db-connection
// No authentication, no method restriction, leaks system config.
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig();
const data = await someInternalDbLogic();
return {
status: 'connected',
config: config.private, // Sensitive leak
preview: data
};
});
The Secure Implementation
To kill Shadow APIs in Nitro: First, audit the `server/api` directory and move any non-endpoint logic into `server/utils` or `server/lib`, which Nitro's router ignores. Second, adopt 'Method-Specific Routing' by naming files with suffixes like `.get.ts` or `.post.ts` to prevent unintended verb execution. Finally, implement a global middleware in `server/middleware/` that acts as a default-deny gatekeeper, requiring valid credentials for any path under `/api/` unless explicitly whitelisted. This ensures that even if a developer accidentally drops a 'test.ts' file into the API folder, it remains inaccessible to unauthorized scanners.
// 1. Move internal logic to server/utils/db.ts (Nitro does not route server/utils) // 2. Use explicit method suffixes (.get.ts) and authentication// server/api/admin/system-check.get.ts export default defineEventHandler(async (event) => { // Ensure user is authenticated via middleware-injected context if (!event.context.user?.isAdmin) { throw createError({ statusCode: 403, statusMessage: ‘Forbidden’ }); }
const data = await someInternalDbLogic(); return { status: ‘healthy’ }; // Return minimal necessary data });
// 3. server/middleware/auth.ts - Global Guard export default defineEventHandler((event) => { const isPublicRoute = [‘/api/login’, ‘/api/health’].includes(event.path); if (event.path.startsWith(‘/api/’) && !isPublicRoute) { const token = getHeader(event, ‘Authorization’); if (!isValid(token)) { throw createError({ statusCode: 401, message: ‘Shadow API Access Blocked’ }); } } });
Your Nitro API
might be exposed to Shadow API Exposure
74% of Nitro 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.