An unthrottled data export endpoint is an unauthenticated bulk-data harvesting vector. A malicious actor with a valid session token — or a compromised account — can trigger unlimited export jobs, each generating a complete archive of user PII. CWE-770 (allocation of resources without limits) applies directly. Even without malicious intent, runaway export requests can exhaust database connections and background job queues. OWASP A05:2021 (Security Misconfiguration) includes absent rate limiting on sensitive endpoints.
Low because exploitation requires an authenticated session, but the potential damage — bulk export of an entire account's PII with no throttle — justifies the control.
Enforce a per-user quota on export requests using Redis or a data_export_jobs table with a lookback window. In your export route (src/app/api/account/export/route.ts):
async function checkExportQuota(userId: string) {
const recentExport = await db.dataExportJob.findFirst({
where: {
userId,
requestedAt: { gte: new Date(Date.now() - 24 * 60 * 60 * 1000) }
}
});
if (recentExport) {
throw new TooManyRequestsError('Export quota: 1 per 24 hours. Try again tomorrow.');
}
}
Return HTTP 429 with a Retry-After header so API clients handle the limit gracefully. Document the quota in the user-facing help text on the export UI.
ID: community-privacy-controls.data-rights.export-rate-limited
Severity: low
What to look for: Enumerate every relevant item. Examine data export endpoints for rate limiting. Check whether repeated export requests are throttled (e.g., max 1 export per hour, max 3 per day). Look for quota enforcement in middleware or API handlers. Verify the limits are documented.
Pass criteria: At least 1 of the following conditions is met. Data export endpoint enforces rate limits (e.g., max frequency 1 per day or per hour, cumulative quota per user). Limits prevent bulk harvesting of user data. Users see rate limit error when quota exceeded.
Fail criteria: No rate limiting on export endpoint. Users can request unlimited exports. No quota enforcement, allowing mass data scraping.
Skip (N/A) when: Never — rate limiting is a standard API protection.
Detail on fail: Describe the rate limit gap. Example: "No rate limiting on /api/export endpoint. Users can request unlimited data exports with no throttling." or "Export requests queued without daily/hourly quota limits; potential for bulk harvesting."
Remediation: Implement rate limiting on export endpoints:
// Using a rate limiting library (e.g., express-rate-limit or redis-based)
import rateLimit from 'express-rate-limit';
const exportLimiter = rateLimit({
windowMs: 24 * 60 * 60 * 1000, // 24-hour window
max: 1, // max 1 request per 24 hours
keyGenerator: (req) => req.user.id,
handler: (req, res) => {
res.status(429).json({ error: 'Export quota exceeded. Try again tomorrow.' });
}
});
app.post('/api/export', exportLimiter, async (req, res) => {
// ... export logic
});
Or with Redis for distributed systems:
async function checkExportQuota(userId: string) {
const key = `export:quota:${userId}`;
const count = await redis.get(key);
if (count && parseInt(count) >= 1) {
throw new Error('Export quota exceeded for today');
}
// Increment counter and set 24-hour expiry
await redis.incr(key);
await redis.expire(key, 86400);
return true;
}