Api Key Exposure in Sails with Api Keys
Api Key Exposure in Sails with Api Keys — how this specific combination creates or exposes the vulnerability
Sails is a Node.js web framework that encourages convention-over-configuration and rapid development. When developers store and reference API keys using patterns typical in Sails — such as placing keys in config/env/*.js, config/keys.js, or loading them via process.env — incorrect access control or accidental exposure can lead to Api Key Exposure. This happens when keys are bundled into client-side assets, logged in detail, or returned by endpoints that should remain internal.
In Sails, configuration files often centralize sensitive values. For example, a typical config/keys.js might export keys for external services:
// config/keys.js
module.exports.keys = {
googleMaps: process.env.GOOGLE_MAPS_API_KEY,
stripe: process.env.STRIPE_SECRET_KEY,
};
If a controller or policy inadvertently passes this configuration object to a view or an error response, the keys can be serialized and sent to the browser. Similarly, if custom actions or blueprints return raw service responses that include headers or debug data containing keys, an attacker who can observe network traffic or error messages may recover the credentials. Logs that include full request/response details are another common source of exposure, especially when environment variables are printed during debugging or monitoring integrations.
Another vector specific to Sails applications is the use of hooks or services that forward requests to third-party APIs. If the integration code does not sanitize outgoing telemetry or does not restrict outbound calls to necessary headers only, keys may leak via Referer headers, client-side JavaScript bundles, or misconfigured proxy settings. Because Sails often serves both API and web views from the same controllers, distinguishing between internal service calls and public endpoints becomes critical to prevent inadvertent key disclosure through cross-origin requests or improperly secured routes.
Middleware and policy chains in Sails can also contribute to exposure when keys are attached to req for downstream use but are not cleared after the request completes. If an error handler serializes req into logs or error pages, embedded keys can be persisted or surfaced to unauthorized viewers. The framework’s flexibility around policies — which can inadvertently allow public access to admin-only endpoints — further increases the likelihood of keys being accessed or echoed in contexts where they should remain protected.
To mitigate these risks, it is essential to audit how keys are referenced across configuration, policies, controllers, and views. Validating that sensitive values are never included in responses, logs, or client-side bundles reduces the likelihood of Api Key Exposure. Tools that scan unauthenticated attack surfaces, such as middleBrick, can identify endpoints that disclose configuration objects or leak credentials in error payloads, helping teams detect exposure early without requiring internal instrumentation.
Api Keys-Specific Remediation in Sails — concrete code fixes
Remediation focuses on ensuring API keys never reach the client, are not logged in full, and are accessed only by trusted server-side code. In Sails, keep keys out of views and response bodies by strictly limiting what data is serialized and returned. Use environment variables and ensure configuration files do not propagate sensitive objects to controllers or services that might expose them.
First, centralize keys in environment-aware configuration without exporting them to the client. For example, use separate config files for server-side defaults and ensure that sensitive exports are omitted from blueprints and views:
// config/env/development.js
module.exports = {
googleMaps: {
apiKey: process.env.GOOGLE_MAPS_API_KEY,
},
};
// config/env/production.js
module.exports = {
googleMaps: {
apiKey: process.env.GOOGLE_MAPS_API_KEY,
},
};
Second, avoid attaching API keys to request objects or including them in logs. Sanitize logging statements and ensure error handlers do not serialize configuration:
// api/services/ExternalService.js
module.exports = {
async fetchData(params) {
const key = sails.config.googleMaps.apiKey;
// Use key only in headers or auth, do not attach to req
const response = await fetch('https://maps.example.com/data', {
headers: { 'x-api-key': key },
});
const data = await response.json();
return data;
},
};
Third, restrict which routes can access key-dependent services by using policies. Define a policy that checks authorization before allowing access to endpoints that require API keys:
// api/policies/requireKey.js
module.exports = async function (req, res, next) {
if (!req.session || !req.session.hasKeyAccess) {
return res.unauthorized('Access to this resource requires valid credentials.');
}
return next();
};
// config/policies.js
module.exports.policies = {
'ExternalController': {
'fetchData': ['requireKey'],
},
};
Finally, validate and sanitize all outbound requests to ensure headers and payloads do not inadvertently include credentials. Use strict header whitelisting and avoid echoing third-party responses that might contain key material:
// api/actions/fetchExternal.js
module.exports.fetchExternal = async function (req, res) {
const apiKey = sails.config.googleMaps.apiKey;
const targetUrl = 'https://api.example.com/resource';
const headers = { 'x-api-key': apiKey };
try {
const response = await fetch(targetUrl, { headers });
if (!response.ok) throw new Error('Upstream failure');
const json = await response.json();
// Never return raw upstream headers
return res.ok({ data: json.safeSubset });
} catch (err) {
return res.serverError('Unable to fetch external data');
}
};
By following these patterns, Sails applications can minimize the risk of Api Key Exposure while maintaining the ability to integrate with external services securely.