Skip to content

feat(auth): add per-IP rate limiting to signup, signin, and password reset#4527

Open
waleedlatif1 wants to merge 1 commit intostagingfrom
waleedlatif1/auth-signup-rate-limit
Open

feat(auth): add per-IP rate limiting to signup, signin, and password reset#4527
waleedlatif1 wants to merge 1 commit intostagingfrom
waleedlatif1/auth-signup-rate-limit

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

Summary

  • Add rateLimit.customRules to better-auth: 3 signups per IP per 10 min, 10 sign-ins per IP per min, 3 password resets per IP per 10 min
  • Configure advanced.ipAddress.ipAddressHeaders with cf-connecting-ip so the limiter sees the real client IP behind Cloudflare (not the ALB)
  • Motivated by today's bot signup wave: 28 of 55 fake accounts came from a single IP — this configuration would have blocked all but the first 3
  • Validated against better-auth@1.3.12 type definitions: rateLimit.customRules, advanced.ipAddress.ipAddressHeaders, and the three endpoint paths (/sign-up/email, /sign-in/email, /forget-password) are all present in the installed version

Type of Change

  • Improvement / hardening

Testing

Tested manually — verified the diff is scoped to apps/sim/lib/auth/auth.ts only. Rate limit defaults to in-memory storage (per-ECS-task); follow-up to consider storage: 'database' for shared-state enforcement across pods if needed.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link
Copy Markdown

vercel Bot commented May 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped May 9, 2026 2:03am

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 9, 2026

PR Summary

Medium Risk
Touches authentication endpoints by introducing per-IP rate limiting and custom client IP header handling, which could inadvertently block legitimate users if the proxy/IP headers are misconfigured. Scope is small and configuration-only, but impacts login/signup availability.

Overview
Adds per-IP request throttling to Better Auth by enabling rateLimit with custom rules for sign-up, sign-in, and forget-password email endpoints.

Configures Better Auth IP detection to trust cf-connecting-ip (and x-forwarded-for) so rate limiting keys off the real client IP when behind Cloudflare.

Reviewed by Cursor Bugbot for commit 91e56a0. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 9, 2026

Greptile Summary

This PR adds per-IP rate limiting to the sign-up, sign-in, and password-reset endpoints in better-auth, and configures cf-connecting-ip as the preferred header for extracting the real client IP behind Cloudflare. It is a direct response to a bot signup wave where 28 of 55 fake accounts originated from a single IP.

  • Rate limits added: 3 signups per IP per 10 min, 10 sign-ins per IP per 1 min, 3 password resets per IP per 10 min — reasonable defaults for the stated threat.
  • IP header config: cf-connecting-ip is set as the primary source (correct for Cloudflare), but x-forwarded-for is included as a fallback, which is spoofable via AWS ALB on requests that bypass Cloudflare (see inline comment).
  • Known limitation: Rate limit state is in-memory and per-ECS-task; the PR description acknowledges a follow-up to add shared storage (database/Redis) for cluster-wide enforcement.

Confidence Score: 3/5

The change adds meaningful protection against the observed bot wave but introduces a spoofing vector that could let a determined attacker bypass the rate limit entirely on direct-to-ALB traffic.

The x-forwarded-for fallback means the protection disappears for any request that skips Cloudflare — an attacker can rotate through arbitrary forged IPs via ALB's append-not-strip behavior, defeating the very control this PR is trying to add. The in-memory, per-task storage is a secondary limitation that weakens the limits even for traffic that does flow through Cloudflare. Both issues are fixable with small follow-up changes, but the spoofing path is present today.

apps/sim/lib/auth/auth.ts — specifically the ipAddressHeaders array and the absence of a shared rate-limit storage backend.

Security Review

  • IP header spoofing (apps/sim/lib/auth/auth.ts, line 194): x-forwarded-for is included as a fallback IP source alongside cf-connecting-ip. AWS ALB appends but does not strip client-controlled X-Forwarded-For values, so any request that reaches the ALB without passing through Cloudflare can forge an arbitrary source IP and bypass the rate limit entirely.

Important Files Changed

Filename Overview
apps/sim/lib/auth/auth.ts Adds per-IP rate limiting for signup, signin, and password-reset endpoints, and configures cf-connecting-ip as the preferred IP header — but including x-forwarded-for as a fallback introduces a spoofing vector on direct-to-ALB traffic; in-memory storage also means limits are per-task, not cluster-wide.

Reviews (1): Last reviewed commit: "feat(auth): add per-IP rate limiting to ..." | Re-trigger Greptile

Comment thread apps/sim/lib/auth/auth.ts
},
advanced: {
ipAddress: {
ipAddressHeaders: ['cf-connecting-ip', 'x-forwarded-for'],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security x-forwarded-for fallback enables rate-limit bypass

Including x-forwarded-for as a fallback header is exploitable when traffic reaches the ALB without going through Cloudflare. AWS ALB appends (but does not strip) client-supplied X-Forwarded-For values, so an attacker who reaches the ALB directly can send X-Forwarded-For: 1.1.1.1 and the ALB produces X-Forwarded-For: 1.1.1.1, <real-client-ip>. Better-auth reads the leftmost value, treating 1.1.1.1 as the client IP — a different spoofed address for every request, defeating the limit entirely. If cf-connecting-ip is always present when behind Cloudflare, the fallback only matters for requests that bypass Cloudflare, which are precisely the ones most at risk of IP-header forgery.

Comment thread apps/sim/lib/auth/auth.ts
Comment on lines +184 to +191
rateLimit: {
enabled: true,
customRules: {
'/sign-up/email': { window: 600, max: 3 },
'/sign-in/email': { window: 60, max: 10 },
'/forget-password': { window: 600, max: 3 },
},
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 In-memory rate limit state is per-ECS-task, not shared

The rate limiter defaults to in-memory storage, so each ECS task maintains its own counters. With N tasks running, any IP effectively gets max * N attempts before being blocked — e.g., 3 signups × 5 tasks = 15 signups unchecked cluster-wide. The PR description already flags this as a known follow-up, but it is worth noting explicitly: against a coordinated signup bot that distributes requests across tasks, the limit provides weaker-than-stated protection until storage: 'database' (or Redis) is added.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant