Api Rate Abuse in Koa with Saml
Api Rate Abuse in Koa with Saml — how this specific combination creates or exposes the vulnerability
Rate abuse in a Koa application that consumes SAML assertions can occur when rate limiting is applied only after SAML validation or is inconsistently enforced across authentication paths. In this combination, an attacker may send a high volume of requests using stolen or forged SAML assertions, or by triggering repeated authentication challenges that bypass incomplete rate controls.
Koa itself does not provide built-in rate limiting; without explicit middleware, endpoints can be called repeatedly. When SAML processing (assertion parsing, signature verification, and user resolution) is performed before or in parallel with rate checks, an attacker can exploit timing differences or missing limits on the authentication flow. For example, a login or SAML endpoint that does not enforce per-subject or per-IP limits allows credential or assertion flooding, leading to denial of service or elevation-of-risk scenarios such as brute-forcing NameID values or session indexes.
Because SAML exchanges often include user identifiers and session indexes, unchecked repeated assertions for the same subject can exhaust application-level session stores or trigger account lockouts. Without correlation between SAML session IDs and rate counters, a scanner may identify missing rate limiting on SAML-consuming routes as a high-severity finding, particularly when assertions are accepted without additional context checks.
Saml-Specific Remediation in Koa — concrete code fixes
Apply rate limiting close to the SAML assertion consumption point and ensure limits are scoped to subjects, sessions, and origins. Use a shared store (e.g., Redis) to coordinate limits across workers and to enforce both per-IP and per-identity constraints.
import Koa from 'koa';
import Router from 'koa-router';
import { parse } from 'fast-saml';
import { RateLimiterRedis } from 'rate-limiter-flexible';
import { redisClient } from './redisClient';
const app = new Koa();
const router = new Router();
// Shared rate limiter for SAML assertion consumption
const limiter = new RateLimiterRedis({
storeClient: redisClient,
keyPrefix: 'saml:',
points: 30, // 30 requests
duration: 60, // per 60 seconds
blockDuration: 300 // block for 5 minutes if exceeded
});
router.post('/saml/consume', async (ctx) => {
const ip = ctx.ip;
const subject = ctx.request.body.NameID || 'unknown';
const keyIp = `ip:${ip}`;
const keySubject = `subject:${subject}`;
try {
// Enforce rate limits at both IP and SAML subject level
await Promise.all([
limiter.consume(keyIp),
limiter.consume(keySubject)
]);
} catch (rateRejected) {
ctx.status = 429;
ctx.body = { error: 'rate limit exceeded', retryIn: rateRejected.msBeforeNext / 1000 };
return;
}
// Minimal SAML assertion processing (example with fast-saml)
try {
const samlResponse = ctx.request.body.SAMLResponse;
const decoded = parse(samlResponse, { /* cert, entryPoint, issuer */ });
if (!decamped || !decamped.valid) {
ctx.status = 401;
ctx.body = { error: 'invalid assertion' };
return;
}
ctx.state.user = decoded.nameID;
ctx.state.sessionIndex = decoded.sessionIndex;
// Continue with application logic
ctx.body = { user: ctx.state.user };
} catch (err) {
ctx.status = 400;
ctx.body = { error: 'saml processing failed' };
}
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000);
In this remediation, rate limiting is applied before SAML parsing to reduce CPU load from malicious assertions, and limits are enforced per IP and per SAML NameID to prevent targeted abuse. Ensure that SAML logout and session invalidation also clear or adjust rate counters to avoid stale limits.
For production, align rate thresholds with your risk profile and monitor for bursts that may indicate token replay or credential testing. The middleBrick CLI can scan this flow and surface missing rate limiting around SAML endpoints, while the GitHub Action can enforce a minimum score before merging changes that modify authentication routes.