Insufficient Logging in Sails with Basic Auth
Insufficient Logging in Sails with Basic Auth — how this specific combination creates or exposes the vulnerability
Sails is a Node.js MVC framework that often relies on policy-based authorization and explicit action logic. When Basic Authentication is used without structured, auditable logs, security-critical events are not recorded in a way that supports detection or incident response. This creates an insufficient logging posture where authentication successes and failures, as well as downstream authorization decisions, are effectively invisible to monitoring tools.
With Basic Auth, credentials are transmitted on every request (albeit base64-encoded). If Sails does not log key details—such as the parsed user identity, the outcome of authentication, timestamp, source IP, requested route, and relevant authorization context—an attacker can probe endpoints with stolen or guessed credentials and leave minimal forensic evidence. For example, repeated 401 responses may indicate credential spraying, while 403 or 500 responses can reveal path traversal or authorization logic flaws, but without logs these patterns remain undetected.
Inadequate log coverage also hampers compliance mapping for frameworks such as OWASP API Top 10 (broken object level authorization), PCI-DSS, and SOC2, where audit trails for authentication and authorization are required. middleBrick’s checks for BOLA/IDOR and Authentication include verifying that sufficient context is captured in logs to reconstruct access decisions. In a Sails application, this means ensuring each authenticated action writes a structured log entry that ties the request to a user identity and outcome, enabling correlation with runtime behavior observed during scanning.
Basic Auth-Specific Remediation in Sails — concrete code fixes
To address insufficient logging while retaining Basic Auth in Sails, instrument authentication and authorization paths to emit structured logs with consistent fields. Avoid logging raw credentials; instead log identifiers and outcomes. Below are concrete examples that integrate with a typical Sails controller and policy setup.
Example 1: Basic Auth parsing helper with safe logging
// api/helpers/basic-auth-helper.js
'use strict';
const crypto = require('crypto');
/**
* Parses and validates Basic Auth header.
* Returns { username, credentialsValid } or null.
* Never logs the password.
*/
module.exports.parseBasicAuth = (req) => {
const authHeader = req.headers.authorization || '';
const match = authHeader.match(/^Basic\s+(\S+)$/i);
if (!match) return null;
const decoded = Buffer.from(match[1], 'base64').toString('utf8');
const separatorIndex = decoded.indexOf(':');
if (separatorIndex === -1) return null;
const username = decoded.slice(0, separatorIndex);
const password = decoded.slice(separatorIndex + 1);
// In real usage, validate credentials against a secure store
const credentialsValid = validateCredentials(username, password);
// Structured log entry without sensitive data
sails.log.info({
timestamp: new Date().toISOString(),
event: 'basic_auth_attempt',
username,
credentialsValid,
ip: req.ip,
method: req.method,
url: req.originalUrl,
});
return credentialsValid ? { username } : null;
};
function validateCredentials(username, password) {
// Replace with secure lookup and constant-time comparison
const users = {
alice: '$2a$10$abc123...', // stored hash
};
const hash = users[username];
if (!hash) return false;
const testHash = crypto.pbkdf2Sync(password, username, 100000, 64, 'sha512').toString('base64');
return testHash === hash;
}
Example 2: Controller using the helper with outcome logging
// api/controllers/AccountController.js
'use strict';
module.exports = {
viewProfile: async function (req, res) {
const auth = sails.helpers.basicAuthHelper(req);
if (!auth) {
sails.log.warn({
timestamp: new Date().toISOString(),
event: 'auth_failure',
reason: 'missing_or_invalid_basic_auth',
ip: req.ip,
method: req.method,
url: req.originalUrl,
});
return res.unauthorized('Invalid credentials');
}
try {
const data = await UserService.getProfile(auth.username);
sails.log.info({
timestamp: new Date().toISOString(),
event: 'profile_accessed',
username: auth.username,
ip: req.ip,
method: req.method,
url: req.originalUrl,
profileId: data.id,
});
return res.ok(data);
} catch (err) {
sails.log.error({
timestamp: new Date().toISOString(),
event: 'profile_access_error',
username: auth.username,
ip: req.ip,
method: req.method,
url: req.originalUrl,
error: err.message,
});
return res.serverError('Unable to load profile');
}
},
};
Example 3: Policy enforcement with audit logging
// api/policies/authenticated-and-logged.js
'use strict';
module.exports = async function (req, res, proceed) {
const auth = sails.helpers.basicAuthHelper(req);
if (!auth) {
sails.log.warn({
timestamp: new Date().toISOString(),
event: 'policy_block_unauthenticated',
path: req.path,
ip: req.ip,
method: req.method,
});
return res.unauthorized('Authentication required');
}
// Proceed to action; action itself should log outcomes as shown above
return proceed();
};
Remediation checklist for Sails + Basic Auth
- Log authentication attempts (success/failure) with username, IP, timestamp, and outcome; never log passwords or full credentials.
- Log authorization decisions at the action or policy level, including the requested resource and result.
- Ensure logs are centralized and retained according to compliance requirements to support detection of BOLA/IDOR or abuse patterns.