fix(api-keys): detrás de Cloudflare la IP que se comparaba era la del edge

El 403 reportaba ip_vista=104.22.14.222, que es un edge de Cloudflare: el
tráfico entra por CF y recién ahí por el proxy del hosting, así que la última
entrada de X-Forwarded-For es el edge y no quien llama.

Se usa CF-Connecting-IP cuando está: la escribe Cloudflare con la IP real y
sobrescribe la que mande el cliente. Sin Cloudflare sigue valiendo la última
entrada del XFF, como estaba.

Queda anotado que todo esto vale mientras el tráfico entre por el proxy: quien
pueda pegarle al origen directo puede falsear los dos encabezados, y eso se
cierra en la red, no en esta función.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-17 21:09:50 -05:00
co-authored by Claude Opus 5
parent dcffab4311
commit 6a3a8c9248
2 changed files with 28 additions and 8 deletions
+11 -4
View File
@@ -12,14 +12,18 @@ func TestIPDelCliente(t *testing.T) {
nombre string
xff string
real string
cf string
want string
}{
{"un solo proxy", "46.202.93.92", "", "46.202.93.92"},
// Con Cloudflare adelante, la última entrada del XFF es el edge de CF
// (104.x.x.x): la IP real solo está en CF-Connecting-IP.
{"detrás de cloudflare", "46.202.93.92, 104.22.14.222", "", "46.202.93.92", "46.202.93.92"},
{"un solo proxy", "46.202.93.92", "", "", "46.202.93.92"},
// Si quien llama manda su propio X-Forwarded-For, el proxy le agrega la
// IP real al final. Quedarse con la primera sería dejar que elija su IP.
{"cliente intentando falsear", "1.2.3.4, 46.202.93.92", "", "46.202.93.92"},
{"con espacios", " 10.0.0.1 , 46.202.93.92 ", "", "46.202.93.92"},
{"sin xff, con x-real-ip", "", "46.202.93.92", "46.202.93.92"},
{"cliente intentando falsear", "1.2.3.4, 46.202.93.92", "", "", "46.202.93.92"},
{"con espacios", " 10.0.0.1 , 46.202.93.92 ", "", "", "46.202.93.92"},
{"sin xff, con x-real-ip", "", "46.202.93.92", "", "46.202.93.92"},
}
app := fiber.New()
@@ -35,6 +39,9 @@ func TestIPDelCliente(t *testing.T) {
if cs.real != "" {
req.Header.Set("X-Real-Ip", cs.real)
}
if cs.cf != "" {
req.Header.Set("CF-Connecting-IP", cs.cf)
}
resp, err := app.Test(req)
if err != nil {
t.Fatalf("%s: %v", cs.nombre, err)