A geocoding API key embedded in client-side JavaScript is visible to any user who opens DevTools — this is CWE-522 (Insufficiently Protected Credentials) and an OWASP A05:2021 (Security Misconfiguration) violation. Exposed keys can be harvested and used by third parties to run geocoding requests against your quota, resulting in unexpected billing spikes or service denial when you hit rate limits. For Google Maps API keys, quota theft has caused five-figure surprise bills. CWE-200 (Exposure of Sensitive Information) applies because the key itself represents privileged access to a paid API service.
Critical because an exposed geocoding API key enables quota theft and service disruption with zero authentication required — any user can extract the key from the browser.
Move all geocoding calls to a server-side API route. The client never receives the API key — it only calls your own backend endpoint.
// src/app/api/geocode/route.ts (Next.js App Router)
export async function POST(req: Request) {
const { address } = await req.json();
const apiKey = process.env.GOOGLE_GEOCODING_API_KEY; // server-only env var
const res = await fetch(
`https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(address)}&key=${apiKey}`
);
const data = await res.json();
if (!data.results?.[0]) {
return Response.json({ error: 'Address not found' }, { status: 400 });
}
const { lat, lng } = data.results[0].geometry.location;
return Response.json({ lat, lng });
}
Ensure GOOGLE_GEOCODING_API_KEY is never prefixed with NEXT_PUBLIC_ — any NEXT_PUBLIC_ variable is bundled into client code. Compare with directory-map-location.geocoding.api-keys-restricted — server-side geocoding eliminates key exposure, but the key should still be restricted in Google Cloud Console.
ID: directory-map-location.geocoding.server-side-geocoding
Severity: critical
What to look for: Search for any client-side geocoding calls (e.g., fetch('https://maps.googleapis.com/maps/api/geocode') in browser code). Check whether geocoding happens via a backend API route. Verify that geocoding API keys are never passed to or accessed by client-side code.
Pass criteria: All geocoding calls originate from server-side code (backend API route, serverless function, cron job). Enumerate all geocoding call sites in the codebase and confirm 0% appear in client-side code. API keys are stored server-side and never exposed to the client. On pass, report the count of server-side geocoding endpoints found.
Fail criteria: Geocoding API is called from client-side code (JavaScript, React component). API keys or service account credentials appear in browser-accessible code. A geocoding call that runs in a useEffect or component body does not count as pass.
Skip (N/A) when: No geocoding or coordinate lookup exists.
Cross-reference: Compare with directory-map-location.geocoding.api-keys-restricted — server-side geocoding prevents key exposure, but keys should still be domain-restricted in the provider console.
Detail on fail: "Geocoding API key exposed in frontend code; client sends requests directly to Google Geocoding API" or "Client-side geocoding function calls https://maps.googleapis.com with API key in URL parameters"
Remediation: Create a backend endpoint for geocoding:
// pages/api/geocode.ts (Next.js)
export default async function handler(req, res) {
const { address } = req.body;
const apiKey = process.env.GOOGLE_GEOCODING_API_KEY;
const response = await fetch(
`https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(address)}&key=${apiKey}`
);
const data = await response.json();
if (data.results && data.results[0]) {
const { lat, lng } = data.results[0].geometry.location;
return res.json({ lat, lng });
}
res.status(400).json({ error: 'Address not found' });
}
Call this from the client-side submission form:
const { lat, lng } = await fetch('/api/geocode', {
method: 'POST',
body: JSON.stringify({ address })
}).then(r => r.json());