Api Key Exposure in Sails (Javascript)
Api Key Exposure in Sails with Javascript — how this specific combination creates or exposes the vulnerability
In Sails applications written in JavaScript, API keys often live in configuration files, environment variables, or are passed into frontend code during builds. Because Sails encourages convention-over-configuration and rapid development, developers sometimes embed keys directly in code or expose them through controller actions and policies. When these keys reach the client—whether through views, JSON responses, or auto-documented endpoints—they become exposed to anyone who can inspect network traffic or source maps.
JavaScript running in Node.js on the server can accidentally leak keys through verbose error messages, debug logs, or improper serialization. Sails blueprints and REST routes may return full model instances that include attributes holding key-like strings if the model definition or model lifecycle callbacks are not carefully scoped. Inconsistent use of select or projection when querying can cause sensitive columns to be included in results served to the client or logged by the framework.
Another common pattern is using environment variables via process.env in Sails configuration files. If these files are committed to version control or served statically, the keys become discoverable. Sails’ hook system can also expose keys if custom hooks or third-party hooks do not properly restrict access to configuration. During local development, the REPL and debug consoles in Sails provide interactive access to the app’s models and configuration, which can inadvertently surface keys if a developer runs commands without considering the security context.
Because Sails can generate OpenAPI specs dynamically or expose API documentation in development mode, keys that appear in query parameters, headers, or request bodies may be reflected in these documents. If authentication is not enforced on documentation routes, an unauthenticated attacker can harvest keys directly from the spec. The JavaScript runtime does not inherently redact sensitive values, so any logging or serialization that includes the full request or response objects can propagate keys to logs, error trackers, or browser consoles.
When conducting an unauthenticated scan with middleBrick, the API Security checks targeting API Key Exposure analyze configuration endpoints, error payloads, and documentation routes. The scanner looks for patterns where keys are returned in JSON structures, present in HTTP headers, or embedded in client-side assets. Findings include the specific route, the method, and the location of the exposure, along with remediation guidance focused on scoping, redaction, and access controls.
Javascript-Specific Remediation in Sails — concrete code fixes
To reduce API key exposure in Sails with JavaScript, start by ensuring keys are never serialized into responses or views. Use selective attributes when fetching records and avoid returning entire model instances to the client. Below are concrete code examples that demonstrate secure patterns.
1. Restrict attributes in blueprint responses
Configure models to exclude sensitive attributes from default JSON output. In your model file, use toJSON to strip keys before serialization.
// api/models/User.js
module.exports = {
attributes: {
username: { type: 'string' },
email: { type: 'string' },
apiKey: { type: 'string', select: false } // hidden by default
},
toJSON: function(obj, ret, options) {
ret.apiKey = undefined;
return ret;
},
toJSONExtended: function(obj, ret, options) {
ret.apiKey = undefined;
return ret;
}
};
2. Use policies to enforce authentication on sensitive routes
Protect endpoints that could expose configuration or key material with a policy that checks for valid session or token presence.
// api/policies/ensureAuthenticated.js
module.exports = function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated && req.user) {
return next();
}
return res.unauthorized('Access denied.');
};
// config/policies.js
module.exports.policies = {
'UserController': {
'viewKey': 'ensureAuthenticated'
}
};
3. Safely load environment variables without exposing them
Keep keys out of version control and avoid logging them. Use environment variables and reference them in configuration without assigning them to global or shared objects inadvertently.
// config/env/development.js
module.exports = {
sails: {
hookTimeout: 5000
},
api: {
key: process.env.API_KEY || 'dev-key-placeholder'
}
};
4. Scopes blueprint actions and limit data returned
Override blueprint actions to return only necessary fields and ensure queries use projection to exclude sensitive columns.
// api/controllers/UserController.js
module.exports = {
findOne: async function(req, res) {
const result = await User.findOne(req.params.id).select(['id', 'username', 'email']);
if (!result) return res.notFound();
return res.ok(result);
}
};
5. Disable or protect API documentation in production
Ensure Swagger/OpenAPI endpoints are not publicly accessible in production and do not reflect sensitive parameter values.
// config/routes.js
module.exports.routes = {
'/docs': {
controller: 'DocumentationController',
skipAssets: true
}
};
// config/swagger.js
module.exports.swagger = {
enabled: false // set to true only in trusted environments
};
6. Sanitize error messages to avoid key leakage
Custom error handlers should avoid echoing raw inputs or configuration details that might contain keys.
// config/errors.js
module.exports.errors = {
custom: {
'E_VALIDATION': function(errors) {
// Return generic messages, never include raw values or keys
return 'Validation error.';
}
}
};