Api Key Exposure in Sails
How Api Key Exposure Manifests in Sails
Api key exposure in Sails applications typically occurs through several distinct attack vectors that leverage the framework's conventions and common implementation patterns. One of the most prevalent issues arises from configuration file management. Sails applications often store API keys in config files like config/local.js or config/production.js, which may inadvertently be committed to version control or deployed to production environments where they become accessible to unauthorized users.
Another common manifestation occurs through error responses that leak sensitive information. Sails's default error handling can expose API keys in stack traces or error messages when exceptions occur during API calls. For instance, if an application makes an external API call using an exposed key and that call fails, the error response might include the full request including the API key in the response body or headers.
Middleware misconfiguration presents another significant risk. Sails applications frequently use middleware for authentication, logging, or request processing. If API keys are passed through request headers or query parameters and the middleware logs requests without proper sanitization, these keys can end up in log files that are accessible to developers or stored in centralized logging systems.
Client-side exposure represents a particularly insidious variant. Sails applications that expose API keys to client-side JavaScript for making direct API calls create a fundamental security vulnerability. Even if the keys are obfuscated or stored in environment variables, they become accessible to anyone who can inspect the browser's network traffic or view the source code.
Environment variable leakage through Sails's configuration system can also expose API keys. While Sails encourages using environment variables for sensitive data, improper configuration can cause these variables to be exposed through the application's configuration endpoints or debug interfaces.
Sails-Specific Detection
Detecting API key exposure in Sails applications requires a multi-faceted approach that combines static analysis with runtime testing. Start by examining your configuration files for hardcoded credentials. Use this command to search for common API key patterns across your Sails project:
grep -r -E "(sk-[a-zA-Z0-9]{20}|pk-[a-zA-Z0-9]{20}|AIza[0-9A-Za-z_-]{35}|Bearer\s+[a-zA-Z0-9\-\._~\+\/]+=*)" config/This regex pattern catches common API key formats from services like Stripe, Google Cloud, and generic Bearer tokens. For a more comprehensive scan, use middleBrick's API scanning capabilities which can detect exposed API keys in runtime environments without requiring access to source code.
middleBrick's scanning engine specifically looks for API key exposure patterns in Sails applications by testing unauthenticated endpoints and analyzing response headers, body content, and error messages. The scanner identifies keys using a database of 500+ known API key patterns and can detect keys even when they're obfuscated or encoded.
Runtime detection should include monitoring your application's error responses. Set up a test endpoint that intentionally triggers errors and examine the response for any exposed credentials. Additionally, review your Sails application's logging configuration to ensure API keys are not being logged in plain text.
For production deployments, implement request logging analysis to detect any API keys appearing in client-accessible responses. Tools like middleBrick can continuously monitor your API endpoints and alert you when new instances of API key exposure are detected.
Sails-Specific Remediation
Remediating API key exposure in Sails applications requires both code changes and process improvements. Start by implementing proper configuration management using Sails's built-in configuration system. Move all API keys to environment variables and access them through sails.config:
// config/apiKeys.js
module.exports.apiKeys = {
stripe: process.env.STRIPE_SECRET_KEY,
twilio: process.env.TWILIO_ACCOUNT_SID,
mapbox: process.env.MAPBOX_API_KEY
};
// policies/apiKeyPolicy.js
module.exports = async function (req, res, next) {
const requiredKeys = ['stripe', 'twilio'];
const missingKeys = requiredKeys.filter(key => !sails.config.apiKeys[key]);
if (missingKeys.length > 0) {
return res.status(500).json({
error: 'Missing API configuration',
missing: missingKeys
});
}
next();
};Implement comprehensive error handling that never exposes API keys in error responses. Create a custom error handler that sanitizes all error messages before sending them to clients:
// api/hooks/customErrorHandler.js
module.exports = function (sails) {
return {
initialize: function (cb) {
sails.on('router:request', (req, res) => {
const originalSend = res.send;
res.send = function (body) {
if (typeof body === 'object' && body.error) {
// Remove any sensitive information from error objects
delete body.error.apiKey;
delete body.error.token;
}
return originalSend.call(this, body);
};
});
cb();
}
};
};Configure Sails's logging system to redact API keys from all log entries. Create a custom logger that automatically masks sensitive information:
// config/log.js
module.exports.log = {
custom: function (msg, data) {
const redacted = JSON.stringify(data, (key, value) => {
if (key.match(/key|token|secret|password/i)) {
return 'REDACTED';
}
return value;
});
sails.log.verbose(msg, JSON.parse(redacted));
}
};For client-side API calls, implement a proxy service that keeps API keys server-side. Create a Sails controller that makes external API calls on behalf of the client:
// api/controllers/apiProxyController.js
module.exports = {
proxyRequest: async function (req, res) {
const { service, endpoint, data } = req.body;
// Validate the requested service
const allowedServices = ['stripe', 'twilio', 'mapbox'];
if (!allowedServices.includes(service)) {
return res.status(400).json({ error: 'Invalid service' });
}
try {
const response = await sails.helpers.makeExternalApiCall(service, endpoint, data);
return res.json(response);
} catch (error) {
return res.status(500).json({ error: 'Proxy request failed' });
}
}
};Finally, implement automated security testing in your CI/CD pipeline using middleBrick's GitHub Action to scan your APIs before deployment:
name: API Security Scan
on: [push, pull_request]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run middleBrick Security Scan
uses: middlebrick/middlebrick-action@v1
with:
api-url: ${{ secrets.API_URL }}
fail-on-severity: high
env:
MIDDLEBRICK_API_KEY: ${{ secrets.MIDDLEBRICK_API_KEY }}