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:
co-authored by
Claude Opus 5
parent
dcffab4311
commit
6a3a8c9248
@@ -12,11 +12,24 @@ import (
|
||||
// devuelve la IP interna del proxy (10.x.x.x), así que ninguna IP pública
|
||||
// autorizada coincidía nunca y toda llave con restricción de IP daba 403.
|
||||
//
|
||||
// Se toma la ÚLTIMA entrada de X-Forwarded-For, no la primera: con un solo
|
||||
// proxy adelante, esa es la que escribió el proxy y no la puede falsear quien
|
||||
// llama. Si el cliente manda su propio X-Forwarded-For, el proxy le agrega la
|
||||
// IP real al final — quedarse con la primera sería creerle al que llama.
|
||||
// Sin Cloudflare se toma la ÚLTIMA entrada de X-Forwarded-For, no la primera:
|
||||
// con un solo proxy adelante, esa es la que escribió el proxy y no la puede
|
||||
// falsear quien llama. Si el cliente manda su propio X-Forwarded-For, el proxy
|
||||
// le agrega la IP real al final — quedarse con la primera sería creerle al que
|
||||
// llama.
|
||||
//
|
||||
// ponytail: esto vale mientras todo el tráfico entre por el proxy (y por
|
||||
// Cloudflare, si está). Quien pueda pegarle al origen directo puede falsear
|
||||
// ambos encabezados; si eso pasa a importar, hay que cerrar el origen a las
|
||||
// redes del proxy en vez de complicar esta función.
|
||||
func IPDelCliente(c *fiber.Ctx) string {
|
||||
// Cloudflare adelante: el proxy del hosting ve la IP del edge de CF, así
|
||||
// que la última entrada de X-Forwarded-For es 104.x.x.x y no el que llama.
|
||||
// CF-Connecting-IP la escribe Cloudflare con la IP real y sobrescribe la
|
||||
// que mande el cliente, así que cuando está es la buena.
|
||||
if cf := strings.TrimSpace(c.Get("CF-Connecting-IP")); cf != "" {
|
||||
return cf
|
||||
}
|
||||
if xff := c.Get("X-Forwarded-For"); xff != "" {
|
||||
partes := strings.Split(xff, ",")
|
||||
if ip := strings.TrimSpace(partes[len(partes)-1]); ip != "" {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user