Server Side Template Injection in Sails with Basic Auth
Server Side Template Injection in Sails with Basic Auth — how this specific combination creates or exposes the vulnerability
Server Side Template Injection (SSTI) occurs when an attacker can inject template code that is subsequently evaluated by the server-side rendering engine. In Sails.js, which can leverage templating engines such as EJS or Pug, unsanitized user input embedded in templates may lead to arbitrary code execution. When Basic Authentication is used without additional safeguards, the attack surface expands because credentials are often handled in controllers or policies before reaching the template layer, and developer assumptions about the safety of authenticated contexts can lead to insufficient input validation.
Consider a Sails controller that renders a profile page using a template engine and incorporates user-supplied data, such as a display name, without proper escaping or validation:
module.exports.profile = function (req, res) {
return res.view('profile', {
name: req.query.name || 'Guest'
});
};
An SSTI payload like ${7*7} in the name query parameter can lead to code execution if the template engine evaluates the expression. With Basic Auth, the request includes an Authorization header (e.g., Authorization: Basic base64(username:password)). If a policy or middleware parses and logs credentials insecurely, or if developer focus on authentication blinds testing of unauthenticated-style inputs, the template may receive attacker-controlled data from headers or cookies in addition to query parameters. This combination means that even when authentication is enforced, SSTI flaws persist because the template processes untrusted input that may originate from parsed credentials or from indirect sources assumed safe due to the authenticated context.
Moreover, Sails applications that expose REST endpoints returning rendered templates can inadvertently amplify SSTI when input validation is limited to presence checks rather than strict allow-lists. Attackers probe endpoints using techniques aligned with the OWASP API Top 10 and API Security Top 10, such as injection patterns categorized under SSTI. The presence of Basic Auth does not mitigate injection; it only ensures identity, not the safety of the data processed by templates. Therefore, the risk persists, and scans using methodologies like those employed by middleBrick can surface these issues by correlating spec definitions with runtime behavior, identifying places where authentication and template rendering intersect without adequate sanitization.
Basic Auth-Specific Remediation in Sails — concrete code fixes
To mitigate SSTI in Sails when using Basic Authentication, treat all user-influenced data—including parsed credentials and header values—as untrusted. Apply output encoding appropriate for the template engine, enforce strict input validation, and avoid embedding raw user input in templates.
1. Use template engine escaping features
Ensure your template engine auto-escapes output. For EJS, use <%= %> instead of <%- %>. For Pug, rely on its default escaping:
// profile.ejs (safe)
<h1>Welcome, <%= name %></h1>
// profile.pug (safe)
h1 Welcome, #{name}
2. Validate and sanitize inputs
Use a validation library or custom checks to enforce allow-lists. Reject or sanitize unexpected characters, especially for names or identifiers that should not contain template syntax:
const escapeHtml = (str) => {
return str.replace(/[&<>"']/g, (match) => ({
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
}[match]));
};
module.exports.profile = function (req, res) {
const rawName = req.query.name;
if (typeof rawName !== 'string' || !/^[
a-zA-Z0-9 _-]{1,50}$/.test(rawName)) {
return res.badRequest('Invalid name');
}
const safeName = escapeHtml(rawName);
return res.view('profile', { name: safeName });
};
3. Secure Basic Auth handling
Parse credentials in policies or controllers without passing raw values directly to templates. Use middleware to authenticate and attach a minimal user object to req, avoiding logging or reflecting credentials in responses:
// api/policies/basicAuth.js
module.exports.basicAuth = function (req, res, next) {
const authHeader = req.headers().authorization;
if (!authHeader || !authHeader.startsWith('Basic ')) {
return res.unauthorized('Missing credentials');
}
const base64 = authHeader.split(' ')[1];
const decoded = Buffer.from(base64, 'base64').toString('utf8');
const [username, password] = decoded.split(':');
// Perform secure verification against a database or service
if (!isValidUser(username, password)) {
return res.unauthorized('Invalid credentials');
}
req.user = { username };
return next();
};
// Example usage in a controller
module.exports.dashboard = function (req, res) {
// req.user is set by the policy; do not echo credentials back
return res.view('dashboard', { username: req.user.username });
};
4. Enforce secure transport and avoid reflection
Ensure HTTPS is used to protect credentials in transit. Never reflect the Authorization header or raw credentials in error messages or templates, as this can aid injection attacks or credential leakage.
By combining these practices—template escaping, strict input validation, secure credential handling, and transport security—you reduce the risk of SSTI in Sails applications using Basic Authentication. Tools like middleBrick can help verify that such controls are effective by scanning endpoints and correlating spec definitions with runtime findings.
Frequently Asked Questions
Does using Basic Authentication prevent Server Side Template Injection in Sails?
How can I test my Sails endpoints for SSTI without credentials?
${7*7} or {{7*7}} depending on the template engine, and inspect responses for code execution evidence. Automated scans, including those that correlate OpenAPI specs with runtime behavior, can help identify vulnerable endpoints.