nginx real_ip_header dan X-Forwarded-For tampaknya salah

59

Deskripsi wikipedia tajuk HTTP X-Forwarded-Foradalah:

X-Diteruskan-Untuk: client1, proxy1, proxy2, ...

Dokumentasi nginx untuk arahan real_ip_headerberbunyi, sebagian:

Arahan ini menetapkan nama header yang digunakan untuk mentransfer alamat IP pengganti.
Dalam hal X-Forwarded-For, modul ini menggunakan ip terakhir di header X-Forwarded-For untuk penggantian. [Tekankan milikku]

Kedua uraian ini tampaknya bertentangan satu sama lain. Dalam skenario kami, X-Forwarded-Forheader persis seperti yang dijelaskan - alamat IP "asli" klien adalah entri paling kiri. Demikian pula, perilaku nginx adalah menggunakan nilai paling kanan - yang jelas merupakan salah satu server proxy kami.

Pemahaman saya X-Real-IPadalah bahwa itu seharusnya digunakan untuk menentukan alamat IP klien yang sebenarnya - bukan proxy. Apakah saya kehilangan sesuatu, atau ini bug di nginx?

Dan, lebih dari itu, apakah ada yang punya saran untuk bagaimana membuat X-Real-IPheader menampilkan nilai paling kiri , seperti yang ditunjukkan oleh definisi X-Forwarded-For?

Kirk Woll
sumber

Jawaban:

97

Saya percaya kunci untuk menyelesaikan masalah X-Forwarded-For ketika beberapa IP dirantai adalah opsi konfigurasi yang baru-baru ini diperkenalkan, real_ip_recursive(ditambahkan dalam nginx 1.2.1 dan 1.3.0). Dari dokumen realgin nginx :

Jika pencarian rekursif diaktifkan, alamat klien asli yang cocok dengan salah satu alamat tepercaya digantikan oleh alamat tidak tepercaya terakhir yang dikirim dalam bidang header permintaan.

nginx mengambil alamat IP terakhir dalam rantai secara default karena itu adalah satu-satunya yang dianggap tepercaya. Tetapi dengan yang baru real_ip_recursivediaktifkan dan dengan beberapa set_real_ip_fromopsi, Anda dapat menentukan beberapa proksi tepercaya dan itu akan mengambil IP terakhir yang tidak tepercaya.

Misalnya, dengan konfigurasi ini:

set_real_ip_from 127.0.0.1;
set_real_ip_from 192.168.2.1;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

Dan header X-Forwarded-For menghasilkan:

X-Forwarded-For: 123.123.123.123, 192.168.2.1, 127.0.0.1

nginx sekarang akan memilih 123.123.123.123 sebagai alamat IP klien.

Adapun mengapa nginx tidak hanya memilih alamat IP paling kiri dan mengharuskan Anda untuk secara eksplisit mendefinisikan proxy tepercaya, itu untuk mencegah spoofing IP mudah.

Katakanlah alamat IP asli klien adalah 123.123.123.123. Anggap saja kliennya tidak bagus, dan mereka mencoba untuk memalsukan alamat IP mereka 11.11.11.11. Mereka mengirim permintaan ke server dengan tajuk ini sudah ada:

X-Forwarded-For: 11.11.11.11

Karena proksi terbalik hanya menambahkan IP ke rantai X-Forwarded-For ini, katakan saja hasilnya akan tampak seperti ini ketika nginx mendapatkannya:

X-Forwarded-For: 11.11.11.11, 123.123.123.123, 192.168.2.1, 127.0.0.1

Jika Anda hanya mengambil alamat paling kiri, itu akan memungkinkan klien untuk dengan mudah menipu alamat IP mereka. Tetapi dengan contoh di atas nginx config, nginx hanya akan mempercayai dua alamat terakhir sebagai proxy. Ini berarti nginx akan memilih dengan benar 123.123.123.123sebagai alamat IP, meskipun IP palsu itu sebenarnya yang paling kiri.

Nick M
sumber
2
Terima kasih banyak untuk ini, itu sangat membantu saya. Ini harus menjadi jawaban yang diterima.
José F. Romaniello
1
Secara default real_ip_header tampaknya X-Real-IP menurut nginx.org/en/docs/http/ngx_http_realip_module.html Apakah itu berarti pengguna jahat dapat mengirim permintaan dengan X-Real-IP acak dan yang akan digunakan sebagai $ remote_addr di nginx (dan juga mungkin diteruskan ke aplikasi)?
gansbrest
@ gansbrest Tidak, karena set_real_ip_from membatasi host tepercaya.
El Yobo
9

Penguraian X-Forwarded-Forheader ini memang cacat pada modul realgin nginx.

len = r->headers_in.x_forwarded_for->value.len;
ip = r->headers_in.x_forwarded_for->value.data;

for (p = ip + len - 1; p > ip; p--) {
  if (*p == ' ' || *p == ',') {
    p++;
    len -= p - ip;
    ip = p;
    break;
  }
}

Ini dimulai di ujung kanan string header, dan segera setelah melihat spasi atau koma, berhenti melihat dan menempelkan bagian di sebelah kanan spasi atau koma dalam variabel IP. Jadi, itu memperlakukan alamat proksi terbaru sebagai alamat klien asli .

Itu tidak bermain bagus sesuai dengan spesifikasi; ini adalah bahaya jika tidak dijelaskan dengan istilah yang jelas menyakitkan di RFC.

Selain itu, bahkan sulit untuk menemukan sumber utama yang baik pada format, yang pada awalnya ditentukan oleh Squid - penggalian melalui dokumentasi mereka mengkonfirmasi pemesanan; paling kiri adalah klien asli, paling kanan adalah append terbaru. Saya sangat tergoda untuk menambahkan [rujukan?] Ke halaman wikipedia itu. Satu suntingan anonim tampaknya menjadi otoritas internet tentang masalah ini.

Jika memungkinkan, dapatkah Anda membuat proksi perantara Anda berhenti menambahkan diri mereka sendiri ke akhir header, hanya menyisakannya dengan alamat klien sebenarnya saja?

Shane Madden
sumber
Terima kasih atas jawabannya, @Shane. Bahkan, ketika mencapai nginx, X-Forwarded-Forsudah ada. (itu adalah alamat IP klien yang benar) nginx sendiri kemudian mulai menambahkan alamat IP penyeimbang beban kami (hop sebelumnya) ke X-Forwarded-Forheader. (mungkin menambahkan apa yang dilihatnya sebagai "alamat jarak jauh") Jika itu tidak berhasil, saya akan dapat menggunakan X-Forwarded-Forheader seperti sebelumnya. (Kami baru saja bermigrasi ke nginx)
Kirk Woll
@Kirk Jadi, ketika nginx mendapatkan header, itu hanya alamat klien asli? Tetapi ketika sedang memprosesnya, itu ditambahkan pada header server proxy yang terhubung? Itu tidak bertambah - satu-satunya waktu yang harus menyentuh header itu adalah ketika mengirim koneksi ke proxy lain melalui a proxy_pass- dan bahkan kemudian, hanya dengan proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;di tempat.
Shane Madden
Bahkan W3C mendapatkan kesalahan ini : dokumentasinya menyatakan "proksi harus menambahkan alamat IP pemrakarsa permintaan ke akhir daftar yang dipisahkan koma dalam bidang header X-Forwarded-For HTTP", harus menyatakan awal .
Ian Kemp
3
@IanKemp, tidak, akhir sudah benar. Ke sisi server proxy, inisiator permintaan (yaitu permintaan TCP ) adalah proxy sebelumnya (jika ada). Bahwa proksi sebelumnya mungkin sudah mengirimkan X-Forwarded-Forheader dengan kemungkinan alamat klien asli di sebelah kiri dan mungkin proksi sebelumnya ditambahkan ke sana. Jadi proksi yang melayani saat ini akan menambahkan proksi sebelumnya (= inisiator) ke akhir daftar itu dan melayani X-Forwarded-Fortajuk yang ditambah itu ke hop hulu berikutnya. Memang, mereka bisa memilih kata-kata yang lebih jelas.
blubberdiblub
5

X-Real-IP adalah alamat IP dari klien sebenarnya yang dibicarakan oleh server (klien "nyata" dari server), yang, dalam kasus koneksi yang diproksi, adalah server proxy. Itu sebabnya X-Real-IP akan berisi IP terakhir di header X-Forwarded-For.

pengguna558061
sumber
1
OK, tapi, bagiku, itu informasi yang tidak pernah berguna. Saya ingin mendapatkan alamat IP asli dari klien - yang penting, dan menurut semua yang saya baca, tujuan dari header ini. Mengapa saya ingin mengetahui alamat IP dari server proxy kami?
Kirk Woll
Jika itu tidak berguna bagi Anda maka itu bukan untuk Anda. Tidak ada yang memaksa Anda untuk menggunakan X-Real-IP. Jika Anda memerlukan IP pengguna di aplikasi Anda, minta parse aplikasi Anda X-Forwarded-For (yang tidak selalu dapat diandalkan karena ada beberapa proxy (alat keamanan internet / firewall) yang tidak mengatur X-Forwarded- Untuk). Dalam konteks nginx, X-Forwarded-For tidak penting karena itu tidak berbicara dengan klien tersebut, selain dari entri terakhir (X-Real-IP) yang merupakan klien nginx. Jika Anda tidak membutuhkannya maka jangan atur, atur, atau abaikan saja: /
user558061
2
Tidak, apa yang saya maksud adalah, mengapa X-Real-IPkembali alamat IP sendiri server proxy saya pernah berguna?
Kirk Woll
Hebat .. jawab pria. Saya mencari informasi yang tepat ini. Saya perlu berbicara dengan server ncat di server proxy saya, jadi saya perlu ini dengan cepat.
Yugal Jindle