Auth Bypass on Vercel
How Auth Bypass Manifests in Vercel
Vercel hosts serverless functions and Next.js API routes under the pages/api/ directory or as Edge Functions. A common auth‑bypass pattern occurs when developers rely on Vercel’s platform‑level protections (e.g., assuming that a route is private because it is not listed in the public UI) but forget to add an explicit authentication check inside the function code. If the function returns sensitive data or performs privileged actions without validating a session token, JWT, or cookie, an unauthenticated attacker can simply call the URL and receive the same response as an authorized user.
Specific code paths where this appears include:
- Next.js API routes that omit middleware such as
getTokenfromnext-auth/jwtor a custom auth guard. - Edge Functions defined in
middleware.tswhere the matcher excludes/api/*paths, leaving those routes unprotected. - Routes exposed through
vercel.jsonrewrites that map a public path to an internal function without adding an auth check in the target function.
These mistakes map directly to OWASP API Security Top 10 2023 category API2:2023 – Broken Authentication. Real‑world examples of similar flaws have been catalogued in CVEs such as CVE‑2021‑22986 (F5 BIG‑IP iControl REST authentication bypass), where missing validation on an API endpoint allowed unauthorized access.
Vercel‑Specific Detection
Because middleBrick performs a black‑box, unauthenticated scan, it can discover these gaps without any credentials or source code. When you submit a Vercel‑hosted URL (e.g., https://my‑project.vercel.app/api/users), middleBrick:
- Sends requests with no authentication headers, cookies, or tokens.
- Checks whether the response returns a
200 OKpayload containing sensitive fields (user IDs, emails, internal keys). - Records the absence of auth challenges (
401 Unauthorizedor403 Forbidden) as a finding under the “Broken Authentication” check. - Provides a severity rating, a remediation hint, and maps the finding to OWASP API2:2023.
You can run the same test locally with the middleBrick CLI:
# Install the CLI (npm)
npm i -g middlebrick
# Scan a Vercel API endpoint
middlebrick scan https://my-project.vercel.app/api/users
The CLI returns JSON or plain‑text output that includes the risk score (A–F), the authentication‑check finding, and step‑by‑step guidance. In CI/CD, the GitHub Action can be added to fail a build if the score drops below a chosen threshold:
name: API Security Check
on: [push]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run middleBrick scan
uses: middlebrick/action@v1
with:
url: https://my-project.vercel.app/api/users
fail-below: B # fail if score worse than B
This gives developers immediate feedback whenever a new route is deployed without proper auth protection.
Vercel‑Specific Remediation
Fixing an auth bypass in Vercel requires adding an explicit authentication check inside the function or middleware. Below are idiomatic, Vercel‑native examples.
1. Protect a Next.js API route with next‑auth
Install the package if not already present:
npm i next-auth
Then validate the session token at the start of the handler:
import { getToken } from 'next-auth/jwt'
import type { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET })
if (!token) {
return res.status(401).json({ error: 'Unauthorized' })
}
// token is valid – proceed with business logic
const user = await db.users.findUnique({ where: { id: token.sub } })
return res.status(200).json(user)
}
2. Secure all API routes with Edge Middleware
Create or update middleware.ts at the project root:
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// This middleware runs on every request
export async function middleware(request: NextRequest) {
const path = request.nextUrl.pathname
// Allow public paths (e.g., /api/public/*) without auth
if (path.startsWith('/api/public/')) {
return NextResponse.next()
}
const token = request.cookies.get('next-auth.session-token')?.value
if (!token) {
// Redirect to sign‑in or return 401 for API calls
if (path.startsWith('/api/')) {
return new NextResponse(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { 'content-type': 'application/json' }
})
}
return NextResponse.redirect(new URL('/api/auth/signin', request.url))
}
// Token present – let the request continue
return NextResponse.next()
}
// Optional: limit middleware to specific paths
export const config = {
matcher: ['/api/:path*', '/dashboard/:path*']
}
3. Guard rewritten paths in vercel.json
If you use rewrites to expose an internal function, add the same auth check inside the target function (as shown above) or protect the rewrite with a header‑based check:
{
"rewrites": [
{ "source": "/internal/data", "destination": "/api/internal/data" }
],
"headers": [
{
"source": "/internal/data",
"headers": [
{ "key": "Cache-Control", "value": "no-store" }
]
}
]
}
After applying these controls, rerun middleBrick (or the GitHub Action) to confirm that the authentication‑check finding disappears and the risk score improves.