HIGH insufficient loggingsailsbearer tokens

Insufficient Logging in Sails with Bearer Tokens

Insufficient Logging in Sails with Bearer Tokens — how this specific combination creates or exposes the vulnerability

In Sails applications, Bearer Tokens are commonly used for stateless authentication, typically passed in the Authorization header as Authorization: Bearer <token>. Insufficient logging in this context means that requests authenticated by Bearer Tokens are not recorded with enough detail to support effective incident investigation, forensic analysis, or anomaly detection. When Sails does not log key elements—such as token identifiers (not the token itself), user context, request paths, timestamps, and response statuses—an attacker’s actions may leave little or no trace.

This gap is especially consequential because Bearer Tokens are often long-lived or improperly scoped; without logs showing which token was used for which action, it is difficult to detect token leakage, replay, or misuse. For example, if an attacker steals a token and makes repeated calls to sensitive endpoints, the absence of token-derived identifiers in logs prevents correlation of malicious patterns. In regulated environments, insufficient logging can also prevent compliance with audit requirements, since there is no verifiable record of who (which token) accessed what and when.

Consider a Sails API endpoint that updates user permissions. If the controller does not log the token context alongside the request, a malicious actor could escalate privileges and the event would be invisible to defenders. Even if request and response bodies are logged, omitting the token’s associated identifier (such as a token ID or user ID extracted from the token payload) means logs cannot be used to trace activity back to a specific credential. This lack of traceability weakens both detection and response capabilities, allowing attacks to persist unnoticed.

Moreover, insufficient logging can intersect with other security checks uncovered by middleBrick. For instance, if an unauthenticated LLM endpoint is present in a Sails service, output that includes sensitive data may be generated without any logged evidence of who requested it or which token was involved. middleBrick’s LLM/AI Security checks specifically probe for system prompt leakage and output exposure; without adequate logging, these findings cannot be effectively monitored or correlated over time.

To mitigate this, Sails applications should log enough metadata to reconstruct events without compromising confidentiality. This includes timestamps, request paths, HTTP methods, status codes, outcome (success/failure), and a non-sensitive token identifier (e.g., a token hash or extracted jti claim). These logs should be centralized and retained according to policy so that security teams can use tools or queries to detect anomalies such as repeated failed authorizations, unusual geographic access, or high-risk endpoints being invoked by a single token.

Bearer Tokens-Specific Remediation in Sails — concrete code fixes

Remediation focuses on ensuring that every request authenticated by a Bearer Token produces actionable, secure logs. In Sails, this is typically implemented via policies that intercept requests before they reach controllers. Below is a concrete policy example that logs essential metadata while avoiding logging the token itself.

// api/policies/secureAuditLog.js
module.exports.secureAuditLog = async function (req, res, next) {
  // Skip logging for non-GET requests if desired, or log all for audit
  const authHeader = req.headers.authorization || '';
  const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : null;
  const tokenHash = token ? crypto.createHash('sha256').update(token).digest('hex') : null;

  const start = Date.now();

  // Ensure we have user context from token verification (e1e.g., from a service)
  const user = req.user || { id: null, roles: [] };

  // Continue to the request so we can log after response is sent
  return next().then(() => {
    const duration = Date.now() - start;
    sails.log.info({
      timestamp: new Date().toISOString(),
      method: req.method,
      url: req.originalUrl,
      status: res.statusCode,
      user: user.id,
      tokenHash: tokenHash,
      roles: user.roles,
      durationMs: duration,
      ip: req.ip
    });
  }).catch((err) => {
    const duration = Date.now() - start;
    sails.log.warn({
      timestamp: new Date().toISOString(),
      method: req.method,
      url: req.originalUrl,
      status: err.status || 500,
      user: user.id,
      tokenHash: tokenHash,
      error: err.message,
      durationMs: duration,
      ip: req.ip
    });
    return res.serverError(err);
  });
};

In this policy, crypto is required at the top of the file (const crypto = require('crypto');). The token is extracted from the Authorization header, hashed, and logged; the actual Bearer Token is never written to logs. User information is expected to be attached to req.user by an earlier authentication policy that verifies the token (e.g., via jsonwebtoken). The policy measures request duration and records the HTTP status, which is valuable for detecting abuse or failures.

For token binding and replay detection, include a token identifier (jti) in your JWT payload and extract it during verification. Then log that identifier instead of or in addition to the hash. Example verification and logging integration:

// api/services/auth.js
const jwt = require('jsonwebtoken');
const crypto = require('crypto');

module.exports.verifyBearerToken = function (token) {
  return new Promise((resolve, reject) => {
    jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'] }, (err, decoded) => {
      if (err) return reject(err);
      // decoded should include 'jti' and 'roles' for logging and authorization
      const auditRef = {
        tokenId: decoded.jti ? crypto.createHash('sha256').update(decoded.jti).digest('hex') : null,
        user: decoded.sub,
        roles: decoded.roles || []
      };
      return resolve(auditRef);
    });
  });
};

Use this service in a policy to set req.user and ensure that subsequent logging in secureAuditLog can reference a stable, non-sensitive identifier. This approach aligns with middleBrick’s findings on Authentication and BOLA/IDOR checks, ensuring that token usage is traceable without exposing secrets.

Finally, apply the policy globally in config/policies.js:

module.exports.policies = {
  '*': ['secureAuditLog'],
  AuthController: { login: false } // if login issues tokens and should not be logged as a privileged action
};

With these changes, Sails will produce logs that support detection of token misuse, facilitate incident response, and satisfy many compliance expectations around auditability.

Frequently Asked Questions

Why should I avoid logging Bearer Tokens directly in Sails?
Logging Bearer Tokens directly risks credential exposure in log stores and can lead to widespread compromise if logs are accessed. Always log a hash or a non-sensitive identifier derived from the token instead.
How does middleBrick help identify insufficient logging related to Bearer Tokens?
middleBrick’s Authentication and BOLA/IDOR checks assess whether token usage is sufficiently recorded. Its LLM/AI Security probes also surface cases where token-derived context is missing, making it hard to trace malicious activity.