Api Key Exposure on Netlify
How Api Key Exposure Manifests in Netlify
Api Key Exposure in Netlify environments typically occurs through several specific patterns that developers encounter when building serverless functions and client-side applications. The most common scenario involves committing API keys directly into source control, which becomes particularly problematic when using Netlify's Git-based deployment model. When a repository is pushed to GitHub, Bitbucket, or GitLab and connected to Netlify, any exposed keys in the codebase are deployed alongside your application.
Netlify Functions present unique challenges for API key management. Developers often hardcode keys in their netlify.toml configuration or directly in functions/src files. For example:
// functions/hello.js - BAD PRACTICE
const apiKey = process.env.REACT_APP_API_KEY; // Wrong environment variable prefix
export async function handler(event) {
const response = await fetch(`https://api.example.com/data?key=${apiKey}`);
return { statusCode: 200, body: JSON.stringify(response) };
}The REACT_APP_ prefix is specifically for Create React App builds, not for Netlify Functions. This mistake exposes the key in client-side bundles when the function is bundled incorrectly.
Another Netlify-specific pattern involves misusing environment variables across the build process. Keys defined in the Netlify UI under 'Build environment variables' are only available during build time, not at runtime for functions. This leads to developers attempting to access them incorrectly:
// functions/api.js - BROKEN APPROACH
const apiKey = process.env.API_KEY; // Available at build time, not runtime
export async function handler(event) {
// This will fail in production
const response = await fetch(`https://api.thirdparty.com/v1`, {
headers: { 'Authorization': `Bearer ${apiKey}` }
});
return { statusCode: 200, body: JSON.stringify(response) };
}Client-side JavaScript deployed through Netlify's static hosting can also leak keys through browser console logs, error messages, or network requests. When keys are embedded in React components or vanilla JavaScript files, they become visible to anyone inspecting the site:
// src/components/DataFetcher.js - EXPOSES KEY TO CLIENTS
const API_KEY = 'sk-1234567890abcdef'; // Visible in bundle.js
export default function DataFetcher() {
const [data, setData] = useState(null);
useEffect(() => {
fetch(`https://api.service.com/data?key=${API_KEY}`)
.then(res => res.json())
.then(setData);
}, []);
return <div>{data ? data.name : 'Loading...'}</div>;
}Netlify's Edge Handlers introduce another attack surface. Developers might inadvertently expose keys by logging sensitive data or including them in responses:
// edge-handlers/auth.js - POTENTIAL LEAK
export async function onRequest(event) {
const apiKey = event.request.headers.get('x-api-key');
console.log(`Received API key: ${apiKey}`); // Logs to Netlify Edge logs
return new Response('Authenticated', { status: 200 });
}Environment variable naming collisions between build-time and runtime contexts create additional exposure risks. Using generic names like API_KEY without proper scoping can lead to accidental exposure through build logs or error messages that include environment variable dumps.
Netlify-Specific Detection
Detecting API key exposure in Netlify environments requires understanding both the deployment pipeline and the specific file structures Netlify uses. The first step is examining your repository for hard-coded keys before deployment. Use grep or similar tools to search for patterns like:
# Search for common API key patterns
grep -r "sk-[a-zA-Z0-9-]" . --exclude-dir=node_modules
# Search for bearer tokens
grep -r "Bearer [a-zA-Z0-9-]" . --exclude-dir=node_modules
# Search for environment variable usage in client code
grep -r "process\.env\." src/ --exclude-dir=node_modulesNetlify's build logs provide valuable detection opportunities. When a build fails or produces warnings, review the complete log output for any exposed keys. Keys appearing in build logs indicate they're being processed in client-side code or improperly configured build steps.
For runtime detection in deployed applications, use browser developer tools to examine network requests. Keys exposed in client-side code will appear in the 'Headers' tab of network requests:
// In browser DevTools console, check for exposed keys
fetch('https://your-netlify-app.netlify.app')
.then(res => res.text())
.then(html => {
// Look for API key patterns in HTML source
const keyPattern = /sk-[a-zA-Z0-9-]{20,}/g;
console.log(keyPattern.exec(html));
});middleBrick provides Netlify-specific scanning that identifies API key exposure across all these vectors. The scanner examines your Netlify Functions directory structure, analyzes netlify.toml configurations, and tests runtime endpoints for exposed credentials. It specifically looks for:
- Hard-coded API keys in JavaScript/TypeScript files
- Misconfigured environment variable usage
- Keys exposed through build processes
- Runtime endpoints that leak credentials in responses
The CLI tool makes this straightforward:
# Install middleBrick CLI
npm install -g middlebrick
# Scan your Netlify functions directory
middlebrick scan ./functions/
# Scan your deployed Netlify site
middlebrick scan https://your-app.netlify.app
# Scan with detailed output for API key detection
middlebrick scan https://your-app.netlify.app --verbosemiddleBrick's LLM/AI Security module also detects AI service keys that might be exposed through OpenAI, Anthropic, or other AI platform integrations common in modern Netlify applications. This includes detecting keys in system prompts or configuration files that could lead to cost exploitation or data exfiltration.
Netlify-Specific Remediation
Remediating API key exposure in Netlify requires leveraging Netlify's native features correctly while following security best practices. The foundation is proper environment variable management through the Netlify UI. Navigate to your site settings > Build & deploy > Environment variables and add runtime-only variables:
# Netlify UI - Add these variables
API_KEY=your-secret-key-here
# Mark as 'Sensitive' to hide from build logs
For Functions, use the correct Netlify Functions pattern with proper environment variable access:
// functions/auth.js - SECURE PATTERN
export async function handler(event, context) {
const apiKey = context.env.API_KEY; // Correct runtime access
const response = await fetch(`https://api.service.com/v1`, {
headers: { 'Authorization': `Bearer ${apiKey}` }
});
return { statusCode: 200, body: JSON.stringify(response) };
}Implement a key rotation strategy using Netlify's deploy contexts. Create different keys for different environments:
# netlify.toml - Context-specific keys
[build.environment]
API_KEY_PRODUCTION = "prod-key-here"
[context.staging.environment]
API_KEY = "staging-key-here"
[context.deploy-preview.environment]
API_KEY = "preview-key-here"
For client-side applications, use Netlify's proxy pattern to keep keys server-side. Create a function that your frontend calls instead of making direct API requests:
// functions/proxy.js - SECURE PROXY PATTERN
export async function handler(event, context) {
const { endpoint } = JSON.parse(event.body);
const apiKey = context.env.API_KEY;
const response = await fetch(endpoint, {
headers: { 'Authorization': `Bearer ${apiKey}` }
});
return { statusCode: 200, body: JSON.stringify(response) };
}Frontend code then calls this proxy function:
// src/api.js - CLIENT-SIDE CALL
export async function fetchProtectedData() {
const response = await fetch('/.netlify/functions/proxy', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ endpoint: 'https://api.service.com/data' })
});
return response.json();
}Implement Netlify Edge Handlers for request filtering and key validation:
// edge-handlers/api-security.js
export async function onRequest(event) {
const request = event.request;
// Validate key format without logging the actual key
if (!apiKey || !/^sk-[a-zA-Z0-9-]{20,}$/.test(apiKey)) {
return new Response('Invalid API key', { status: 401 });
}
> // Forward to backend with validated key
return fetch(request, {
headers: { ...request.headers, 'x-validated-key': 'true' }
});
}Add middleBrick to your CI/CD pipeline to prevent future exposures:
# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run middleBrick Scan
run: |
npm install -g middlebrick
middlebrick scan ./functions/ --fail-on-severity=high
- name: Deploy to Netlify
if: success()
uses: netlify/actions/cli@master
with:
args: deploy --dir=publicFinally, implement monitoring for exposed keys using Netlify's build hooks and error tracking. Set up alerts for any build that includes suspicious patterns or for any runtime errors that might indicate key exposure.