HIGH api rate abusesailsbearer tokens

Api Rate Abuse in Sails with Bearer Tokens

Api Rate Abuse in Sails with Bearer Tokens — how this combination creates or exposes the vulnerability

Rate abuse in Sails when Bearer tokens are used centers on how authentication state is associated with requests. Sails is an MVC framework that typically sits behind a reverse proxy or load balancer. If rate limiting is applied at the framework level without considering the token identity, an attacker can exhaust per-IP or global limits while rotating through valid Bearer tokens to bypass quotas.

Bearer tokens are often issued per user or per client application. In Sails, you might validate a token in a policy (e.g., api/policies/authenticate.js) and attach the payload to req.user. If your rate limiter only keys on IP address (e.g., using a Redis store with an IP-based key), a single IP can repeatedly present different valid tokens to exceed intended limits. This violates the intent of per-user rate controls and can enable credential stuffing, brute-force authentication endpoints, or scraping of protected resources.

Another scenario involves unauthenticated public endpoints that also accept Bearer tokens for elevated privileges. If a route is accessible without a token but accepts one for enhanced permissions, an attacker can make unauthenticated requests to hit global limits while reserving token-authenticated calls for more aggressive abuse. Sails policies that conditionally apply based on token presence can inadvertently create two tiers of behavior that are not reflected in rate-limiting logic.

Real-world attack patterns mirror the OWASP API Top 10 #4:2023 — API Abuse. Consider an endpoint POST /api/v1/transfer that transfers funds and is protected by a Bearer token. Without tying rate limits to the token’s subject (e.g., req.user.id), an attacker with a single token can perform many rapid transfers. Even with token validation, if the rate limiter uses a coarse key like req.ip, distributed attackers can rotate IPs while using the same token pool, or use many tokens to amplify impact.

To detect this with middleBrick, you can submit your Sails API endpoint for a scan. The tool runs 12 parallel checks, including Rate Limiting and Authentication, and cross-references your OpenAPI/Swagger spec (with full $ref resolution) against runtime behavior. It looks for missing or inconsistent rate-limiting scopes, such as limits defined per IP but not per token, and flags authentication bypass risks that enable rate abuse. Findings include severity, a description, and remediation guidance, helping you align limits with identity rather than network perimeter alone.

Bearer Tokens-Specific Remediation in Sails — concrete code fixes

Remediation focuses on ensuring rate limits are keyed by the authenticated identity derived from the Bearer token, not just by IP. In Sails, policies are ideal for this because they run before controllers and can inspect req.headers.authorization.

First, normalize token validation so every protected route sets a stable user identifier on req. Example policy that extracts and verifies a Bearer token, then attaches a user ID:

// api/policies/bearerAuth.js
const jwt = require('jsonwebtoken');

module.exports = async function (req, res, proceed) {
  const auth = req.headers.authorization;
  if (!auth || !auth.startsWith('Bearer ')) {
    return res.unauthorized('Missing Bearer token');
  }
  const token = auth.split(' ')[1];
  try {
    const payload = jwt.verify(token, process.env.JWT_SECRET);
    req.user = { id: payload.sub, role: payload.role };
    return proceed();
  } catch (err) {
    return res.unauthorized('Invalid token');
  }
};

Next, implement rate limiting that uses req.user.id when a token is present, and falls back to IP when unauthenticated. Using a Redis store with the rate-limiter-flexible package:

// api/policies/rateLimit.js
const { RateLimiterRedis } = require('rate-limiter-flexible');
const redisClient = require('redis').createClient({ url: process.env.REDIS_URL });

const rateLimiter = new RateLimiterRedis({
  storeClient: redisClient,
  keyPrefix: 'middlebrick_rl',
  points: 100, // 100 requests
  duration: 60, // per 60 seconds
});

module.exports = async function (req, res, proceed) {
  let key;
  if (req.user && req.user.id) {
    key = `userId:${req.user.id}`;
  } else {
    key = `ip:${req.ip}`;
  }
  try {
    await rateLimiter.consume(key);
    return proceed();
  } catch (rejRes) {
    res.set('Retry-After', rateLimiter.msBeforeNext(key));
    return res.tooManyRequests('Rate limit exceeded');
  }
};

For routes where unauthenticated and authenticated behavior must be distinctly limited, define separate point budgets. For example, authenticated users get 1000 requests per hour, while IP-based limits for public endpoints are 100 requests per hour. In your routes config (config/routes.js), apply the policy selectively:

// config/routes.js
module.exports.routes = {
  'POST /api/v1/transfer': {
    policy: ['bearerAuth', 'rateLimit'], // uses user-id keyed limit
    controller: 'TransferController.transfer',
  },
  'GET /api/v1/public': {
    policy: ['rateLimit'], // uses IP keyed limit
    controller: 'PublicController.list',
  },
};

When using the middleBrick CLI (middlebrick scan <url>) or GitHub Action, you can validate that your rate-limiting policy aligns with token identity. The scan will highlight mismatches such as rate limits defined only by IP while tokens are used for authentication, supporting compliance mappings to OWASP API Top 10 and frameworks like PCI-DSS.

Frequently Asked Questions

How can I verify my Sails rate limits are tied to Bearer token identity and not just IP?
Use the middleBrick CLI to scan your endpoint: middlebrick scan https://api.example.com. Check the Rate Limiting and Authentication findings; they will flag if limits rely solely on IP while tokens are accepted. Complement this with integration tests that send requests with different valid tokens from the same IP and confirm distinct limit buckets.
What is a safe fallback if Redis for rate limiting is unavailable in Sails?
Configure a memory fallback with strict point reductions and shorter durations, but treat it as a degraded mode. For example, reduce points to 10% and duration to 60 seconds, and emit alerts. Ensure your policies detect the fallback and log warnings so you can remediate Redis connectivity without exposing higher risk.