Api Rate Abuse in Adonisjs with Dynamodb
Api Rate Abuse in Adonisjs with Dynamodb — how this specific combination creates or exposes the vulnerability
AdonisJS does not enforce rate limits by default, which can allow a client to make repeated requests against an endpoint that reads from or writes to DynamoDB. When DynamoDB is used as the persistence layer, an attacker can trigger excessive provisioned capacity consumption, amplify latency through conditional writes, and increase the risk of exhausting available connections or partitions. Because AdonisJS routes typically handle authentication and business logic in controllers, missing or weak rate limiting enables rapid, unthrottled calls that interact with DynamoDB operations such as batch writes, query scans, or conditional updates.
An example pattern that can be abused is a user registration or data submission endpoint that calls doc.save() or Doc.query().where('index', 'eq', value) without any request throttling. If the DynamoDB table relies on provisioned mode, repeated writes can inflate consumed write capacity units (WCUs), potentially degrading performance for other users. If the table uses on-demand capacity, costs can rise unexpectedly due to high request rates. Because the application layer does not enforce rate limits, DynamoDB metrics such as ConsumedWriteCapacityUnits and SystemErrors can spike, and throttling responses from DynamoDB may cascade into retries that further increase load.
In a distributed environment behind a load balancer or serverless adapter, AdonisJS instances may each maintain independent in-memory counters or lack shared state, making token-bucket or sliding-window enforcement inconsistent. An attacker can rotate source IPs or use parallel requests to bypass host-level limits, especially when no global policy is enforced at an edge or API gateway. DynamoDB streams or auto-scaling events may lag, allowing bursts to slip through before alarms trigger. The framework’s middleware stack can inadvertently permit unauthenticated or under-scoped access to high-cost operations, such as scanning a large table or executing a query with a non-indexed attribute, which compounds the abuse surface.
Because middleBrick checks rate limiting as one of its 12 parallel security validations, it can surface missing or weak controls in AdonisJS applications that interact with DynamoDB. Findings include missing per-user or per-IP limits, missing burst controls, and missing enforcement on mutation endpoints. Remediation guidance centers on implementing robust, shared-rate enforcement and validating that DynamoDB usage remains within expected operational bounds.
Dynamodb-Specific Remediation in Adonisjs — concrete code fixes
To mitigate rate abuse in AdonisJS when using DynamoDB, apply rate limiting at the framework level and add defensive checks before DynamoDB operations. Use a shared store such as Redis to coordinate limits across instances, and enforce limits on both read and write paths. The following patterns assume you have installed the @adonisjs/redis package and configured a Redis connection in config/redis.js.
First, create a reusable rate-limiter service that uses Redis tokens:
// start/services/RateLimiter.ts
import { Redis } from '@adonisjs/redis/types/redis'
export class RateLimiter {
constructor(protected redis: Redis) {}
async allow(key: string, limit: number, windowSeconds: number): Promise<boolean> {
const now = Date.now()
const windowStart = now - windowSeconds * 1000
const tokenKey = `rate:${key}`
// Remove outdated timestamps and count current window
await this.redis.client.zRemRangeByScore(tokenKey, '0', (windowStart).toString())
const current = await this.redis.client.zCard(tokenKey)
if (current > limit) {
return false
}
await this.redis.client.zAdd(tokenKey, { score: now, value: now.toString() })
await this.redis.client.expire(tokenKey, windowSeconds)
return true
}
}
Then, apply the limiter in a controller handling DynamoDB writes, such as creating or updating items:
// start/controllers/ItemsController.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
import { DynamoDBDocumentClient, PutCommandInput } from '@aws-sdk/lib-dynamodb'
import { dynamoDb } from '@ioc:Adonis/Addons/Aws'
import RateLimiter from 'App/Services/RateLimiter'
export default class ItemsController {
protected limiter = new RateLimiter(this.container.use('Redis'))
public async create({ request, response }: HttpContextContract) {
const key = `user:${request.auth().user?.id}:writes`
const allowed = await this.limiter.allow(key, 30, 60) // 30 writes per minute
if (!allowed) {
return response.status(429).send({ error: 'rate limit exceeded' })
}
const params: PutCommandInput = {
TableName: process.env.DYNAMO_TABLE,
Item: {
id: request.input('id'),
ownerId: request.auth().user?.id,
data: request.only(['name', 'value']),
createdAt: new Date().toISOString(),
},
}
try {
await dynamoDb.send(new PutCommandInput(params))
return response.created({ id: params.Item.id })
} catch (error) {
// handle ConditionalCheckFailedException, ProvisionedThroughputExceededException, etc.
return response.internalServerError({ error: 'DynamoDB error' })
}
}
}
For read-heavy endpoints, enforce a separate limit and add a small backoff to reduce bursts that can amplify DynamoDB read activity:
// start/controllers/SearchController.ts
public async index({ request, response }: HttpContextContract) {
const key = `ip:${request.ip()}:queries`
const allowed = await this.limiter.allow(key, 100, 60) // 100 queries per minute
if (!allowed) {
return response.status(429).send({ error: 'rate limit exceeded' })
}
const { q } = request.qs()
const command = new QueryCommand({
TableName: process.env.DYNAMO_TABLE,
IndexName: 'GSI_NAME',
KeyConditionExpression: 'gsi_pk = :pk AND gsi_sk = :sk',
ExpressionAttributeValues: {
':pk': `search#${q}`,
':sk': 'meta',
},
})
const { Items } = await dynamoDb.send(command)
return response.ok({ results: Items || [] })
}
To align with middleBrick’s findings, ensure that limits are applied before DynamoDB client initialization, that you monitor ConsumedReadCapacityUnits and ConsumedWriteCapacityUnits via CloudWatch, and that you implement exponential backoff on the client side for ProvisionedThroughputExceededException. MiddleBrick’s scan can validate that rate limiting is present on sensitive routes and that DynamoDB interactions do not lack basic throttling controls.