I've seen ActionDispatch::RemoteIp::IpSpoofAttackError a few times and while Claude always assures me it's fine, I wanted to understand what it actually is.
In simple terms: Attackers want valuable stuff. We don't want them to get valuable stuff. A great way to get valuable stuff is to use lists of stolen username/passwords to brute force login forms (or other forms with valuable stuff). However, most websites (with a decent security posture) rate-limit sensitive forms to prevent this very type of abuse. But you rate-limit by checking IP addresses, and how you check those IP addresses is important. An IP spoofing attack is an attempt to exploit a website with bad IP address checking logic.
The ActionDispatch error above happens when the Client-IP header doesn't match the X-Forwarded-For header. The Client-IP can be set to whatever the requestor wants, whereas the X-Forwarded-For header is a chain that trusted proxies will only append to.1
Let's illustrate what this spoofed IP request might look like:
Client-IP
210.63.163.15
(fake)
X-Forwarded-For
134.25.92.227, 195.178.110.72, 104.23.243.122, 10.31.184.41
(also fake) (real sender) (Cloudflare) (internal)
The attacker can put whatever they want in Client-IP, and they can put whatever they want in X-Forwarded-For. However, when the request arrives at Cloudflare (or any trusted proxy), Cloudflare will append the real IP address to the X-Forwarded-For chain, and then your infrastructure host will likely append an internal IP address as they forward the request through their VPC to your service.
Rails does the following:
- First, it attempts to find the
Client-IPvalue anywhere in theX-Forwarded-Forchain. If it cannot find that value anywhere in the chain, it raises theActionDispatch::RemoteIp::IpSpoofAttackErrorexception. - Only if it does find a match, Rails then locates the visitor's real IP address by starting at the end of the chain and moving back until it finds the first untrusted IP address.
If you haven't configured Cloudflare as a trusted proxy, Rails will see Cloudflare's IP as the first untrusted IP address, so that will be the "visitor's IP address". Rails will still ignore the request—preventing the attack—BUT this means your IP-based rate limiting has a bug: multiple people connecting through the same Cloudflare IP address will be rate limited as the same person. Typically not a big deal for normal usage, but can bite you on forms with very low rate limits.
I wrote this to build my own understanding about this error. I am also ignoring this exception in Honeybadger because it's just noise.
# honeybadger.yml
exceptions:
ignore:
- ActionDispatch::RemoteIp::IpSpoofAttackError
-
Technically, the X-Forwarded-For header can also be set to whatever the requestor wants, but trusted (read: not run by malicious actors) proxies will only ever append. ↩