RL
GitHub
Documentation

Security Guide

IP spoofing, trusted proxies, and login/OTP limits.

Security Guide: IP Spoofing, Trusted Proxies, and Login/OTP Limits

This guide explains how the rate limiter identifies client IPs, how to configure trusted proxies safely, and how to harden login or OTP endpoints against brute force attacks.

Prerequisites

  • Access to rate-limit.security settings in application.yaml
  • A clear understanding of your proxy chain (load balancer, CDN, ingress)

IP spoofing and why it matters

If your service trusts X-Forwarded-For from any source, a client can set that header to any IP they want. That breaks IP-based limits because the attacker can rotate spoofed IPs on every request.

The system avoids this by only trusting proxy headers when the request came from a known proxy. If the remote address is not in the trusted proxy list, the rate limiter ignores proxy headers and uses request.getRemoteAddr().

Configure trusted proxies correctly

Set trusted proxies to the IPs or CIDR ranges of your reverse proxies and load balancers. Do not include public client ranges. If you use a CDN, add the CDN edge IP ranges.

yaml
rate-limit:
  security:
    enabled: true
    trusted-proxies:
      - 127.0.0.1
      - 10.0.0.0/8
      - 172.16.0.0/12
      - 192.168.0.0/16

trusted-proxies controls whether proxy headers are trusted at all. If a request is not from one of these ranges, the system will ignore X-Forwarded-For and X-Real-IP and fall back to the TCP remote address. This is the core defense against IP spoofing.

If you misconfigure this list and include public client ranges, you effectively allow spoofing. If you omit a proxy range, the rate limiter will treat all traffic as originating from the proxy itself, collapsing many users into one IP and causing false positives.

Header precedence and proxy chains

The system checks client IP in this order:

  1. X-Forwarded-For (uses the rightmost non-private IP)
  2. X-Real-IP
  3. CF-Connecting-IP
  4. request.getRemoteAddr()

This order matches common proxy setups. If you are behind multiple proxies, ensure they append to X-Forwarded-For rather than overwriting it. A trusted proxy should add its own address to the chain so the original client IP remains visible.

Rate-limiting login and OTP endpoints

Login and OTP flows are security-sensitive, so use the strictest enforcement. Sliding Window Log is preferred because it enforces limits in any rolling window and removes boundary spikes.

Apply limits by both IP and a user identifier. For example, rate-limit requests by IP and also by username or userId to reduce the impact of NATs and shared networks.

yaml
rate-limit:
  endpoints:
    - path: /auth/login
      user-tiers:
        default:
          algorithm: SLIDING_WINDOW
          maxRequests: 10
          windowTime: 60000
    - path: /auth/otp
      user-tiers:
        default:
          algorithm: SLIDING_WINDOW
          maxRequests: 5
          windowTime: 300000

This example limits login to 10 requests per minute and OTP to 5 requests per 5 minutes. These are realistic starting points but should be tuned based on your risk model. If you set the limits too low, you will lock out legitimate users behind shared networks; too high and brute force becomes practical.

If a client exceeds the limit, the response is 429 Too Many Requests with X-RateLimit-ResetOn. Clients should back off until that timestamp.

Trusted tier selection

Do not trust the client to declare its own tier. The config endpoint accepts userTier, but that is a configuration input, not a runtime identity. The rate limiter should derive tier from your authentication layer (JWT claims, API keys, or a trusted user service) and inject it into the request context.

If you let clients select their own tier, they can bypass limits by claiming premium or enterprise. Always treat tier as server-side data.

What to verify

You should be able to:

  • See correct client IPs when requests pass through trusted proxies.
  • Trigger a 429 when exceeding login or OTP limits.
  • Confirm X-RateLimit-ResetOn lines up with your configured window.

If IPs look wrong, fix the proxy chain first before adjusting limits. Incorrect IP extraction breaks every other security control built on top of it.