HIGH api key exposurevercel

Api Key Exposure on Vercel

How Api Key Exposure Manifests in Vercel

Api key exposure in Vercel deployments typically occurs through three primary vectors: environment variable leakage in client-side code, misconfigured API routes, and Next.js API middleware that inadvertently exposes secrets.

The most common scenario involves developers using process.env in Next.js getServerSideProps or getStaticProps without understanding that these props get serialized into the HTML response. Consider this vulnerable pattern:

// pages/api/secret.js
export default function handler(req, res) {
  const apiKey = process.env.API_KEY;
  res.status(200).json({ apiKey });
}

// pages/index.js
export async function getServerSideProps() {
  const response = await fetch('https://api.example.com/data', {
    headers: {
      'Authorization': `Bearer ${process.env.API_KEY}`
    }
  });
  
  return {
    props: {
      data: await response.json(),
      apiKey: process.env.API_KEY // EXPOSED!
    }
  };
}

export default function Home({ data, apiKey }) {
  // apiKey is now in the browser
  return 
{JSON.stringify(data)}
; }

Another Vercel-specific vector is the API_ROUTE_API_KEY header exposure. When using Vercel's built-in API routes, developers sometimes log or return headers that contain API keys:

// pages/api/external.js
export default async function handler(req, res) {
  const response = await fetch('https://api.example.com/data', {
    headers: {
      'Authorization': `Bearer ${process.env.API_KEY}`,
      'API_ROUTE_API_KEY': process.env.API_KEY // Auto-injected by Vercel
    }
  });
  
  // Logging headers exposes the key
  console.log(req.headers);
  
  res.status(200).json(await response.json());
}

Vercel's edge functions can also inadvertently expose keys through improper caching. When using cache-control headers without proper isolation, API keys in response bodies can be cached and served to unauthorized users:

// pages/edge.js
export default async function EdgeAPIRequest() {
  const apiKey = process.env.API_KEY;
  
  return new Response(JSON.stringify({ apiKey }), {
    status: 200,
    headers: {
      'Content-Type': 'application/json',
      'Cache-Control': 'public, max-age=60' // Caches the API key!
    }
  });
}

Vercel-Specific Detection

Detecting API key exposure in Vercel deployments requires both static analysis and runtime scanning. middleBrick's black-box scanner is particularly effective here because it can detect exposed keys without requiring source code access.

For manual detection, start by examining your build output. Vercel's deployment logs show the final bundled JavaScript, making it easy to spot process.env usage that might expose secrets:

# Check build logs for exposed environment variables
vercel logs --limit 50

# Search for process.env in the deployed bundle
curl -s https://your-app.vercel.app/_next/static/chunks/main.js | grep -o "process\.env\.[^)]*"

middleBrick specifically scans for Vercel's auto-injected headers and environment variable patterns. The scanner tests for:

  • Exposed API_ROUTE_API_KEY headers in API responses
  • Serialized process.env variables in HTML responses
  • Environment variable leakage through error messages and stack traces
  • Cache poisoning that serves API keys to unauthorized users

Here's how to run middleBrick against your Vercel deployment:

# Install the CLI
npm install -g middlebrick

# Scan your deployed Vercel app
middlebrick scan https://your-app.vercel.app --output json

# Scan with specific focus on API routes
middlebrick scan https://your-app.vercel.app/api --category api-security

The scanner's LLM/AI security module also checks for AI-specific exposure patterns, such as system prompts containing API keys in AI-powered features:

// Vulnerable AI endpoint
export default async function handler(req, res) {
  const systemPrompt = `Your API key is ${process.env.API_KEY}. Use it responsibly.`;
  const prompt = req.body.prompt;
  
  const response = await aiModel.generate({ systemPrompt, prompt });
  res.status(200).json(response);
}

Vercel-Specific Remediation

Fixing API key exposure in Vercel requires understanding Next.js's data flow and using Vercel's built-in security features. The most effective approach is to ensure secrets never reach the client side.

For API routes, always validate that environment variables aren't returned in responses:

// pages/api/external.js
export default async function handler(req, res) {
  const apiKey = process.env.API_KEY;
  
  // Only use the key internally, never return it
  const response = await fetch('https://api.example.com/data', {
    headers: {
      'Authorization': `Bearer ${apiKey}`
    }
  });
  
  const data = await response.json();
  
  // Return only what the client needs
  res.status(200).json({
    data: data.results,
    metadata: data.metadata
  });
}

For pages that need API data, use client-side fetching instead of server-side props when dealing with secrets:

// pages/index.js
export default function Home() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    const fetchProtectedData = async () => {
      try {
        const response = await fetch('/api/external');
        const result = await response.json();
        setData(result);
      } catch (error) {
        console.error('Failed to fetch data:', error);
      } finally {
        setLoading(false);
      }
    };
    
    fetchProtectedData();
  }, []);
  
  if (loading) return 
Loading...
; return
{JSON.stringify(data)}
; }

Vercel's middleware.ts can also help prevent exposure by blocking requests that might reveal secrets:

// middleware.ts
import { NextResponse } from 'next/server';

export function middleware(request) {
  const url = request.nextUrl;
  
  // Block requests to sensitive endpoints from unauthorized sources
  if (url.pathname.startsWith('/api/secret') && !request.headers.get('x-internal-call')) {
    return new NextResponse(null, {
      status: 403,
      statusText: 'Forbidden'
    });
  }
  
  return NextResponse.next();
}

export const config = {
  matcher: ['/api/:path*']
};

For edge functions, use Vercel's Response constructor with proper content security:

// pages/edge.js
export default async function EdgeAPIRequest() {
  const apiKey = process.env.API_KEY;
  
  // Process data without exposing the key
  const processedData = await processDataWithApiKey(apiKey);
  
  return new Response(JSON.stringify(processedData), {
    status: 200,
    headers: {
      'Content-Type': 'application/json',
      'Cache-Control': 'private, no-store' // Never cache sensitive data
    }
  });
}

Frequently Asked Questions

How can I test if my Vercel API routes are leaking API keys?
Use middleBrick's black-box scanner to automatically detect exposed API keys in your deployed Vercel application. The scanner tests for common exposure patterns including serialized environment variables, exposed headers, and cached responses containing secrets. You can also manually inspect your build output for process.env usage and test API endpoints directly to verify they don't return sensitive data.
What's the difference between <code>getServerSideProps</code> and <code>getStaticProps</code> regarding API key exposure?
Both can expose API keys if you include environment variables in the returned props. getStaticProps runs at build time and can embed keys in the HTML if not careful. getServerSideProps runs on each request but still serializes all props to the client. The safest approach is to use client-side fetching for any data that requires API keys, ensuring secrets never reach the browser.