1 poin oleh GN⁺ 6 jam lalu | 2 komentar | Bagikan ke WhatsApp
  • Sebagai protokol proxy yang meneruskan permintaan ke backend yang berjalan jangka panjang melalui soket, ini dapat diterapkan tanpa banyak mengubah struktur handler HTTP yang sudah ada
  • Reverse proxy HTTP/1.1 mudah mengalami perbedaan interpretasi batas pesan antar implementasi, sehingga terus memunculkan masalah keamanan serius seperti desync dan request smuggling
  • FastCGI telah menyediakan framing pesan yang jelas sejak 1996, dan secara struktural memisahkan header klien dari informasi tepercaya yang ditambahkan proxy
  • net/http/fcgi di Go mengisi REMOTE_ADDR ke Request.RemoteAddr dan juga mencerminkan status HTTPS ke Request.TLS, sehingga penyampaian informasi tepercaya dapat ditangani tanpa middleware terpisah
  • Ada keterbatasan seperti tidak mendukung WebSockets, ekosistem alat yang lemah, dan throughput lebih rendah pada sebagian workload, tetapi jika WebSockets tidak diperlukan dan performanya sudah memadai, ini masih tampak sebagai pilihan yang praktis

Posisi dan cara penerapan FastCGI

  • FastCGI tidak hanya digunakan untuk model eksekusi proses per file, tetapi juga bisa dipakai sebagai protokol proxy-backend yang mengirim permintaan ke daemon yang berjalan jangka panjang melalui soket TCP atau UNIX
  • Di Go, ini bisa diterapkan cukup dengan mengimpor paket net/http/fcgi dan mengganti http.Serve menjadi fcgi.Serve
    • Handler yang sudah ada tetap menggunakan http.ResponseWriter dan http.Request
    • Struktur aplikasi lainnya juga tetap dipertahankan
  • Proxy utama seperti Apache, Caddy, nginx, dan HAProxy mendukung backend FastCGI, dan konfigurasinya juga relatif sederhana

Masalah parsing saat memakai HTTP sebagai protokol backend

  • Reverse proxy HTTP nyaris seperti ladang ranjau keamanan, dan masalah seperti kerentanan desync pada proxy media Discord yang memungkinkan mengintip lampiran privat terus bermunculan
  • HTTP/1.1 terlihat seperti protokol teks yang sederhana, tetapi memiliki terlalu banyak cara untuk merepresentasikan pesan yang sama dan terlalu banyak pengecualian, sehingga tiap implementasi mudah menafsirkannya secara berbeda
  • Masalah terbesarnya adalah HTTP tidak memiliki framing eksplisit untuk pesan
    • Akhir pesan dijelaskan oleh pesan itu sendiri dengan berbagai cara
    • Tiap implementasi dapat menafsirkan titik akhir satu pesan dan awal pesan berikutnya secara berbeda
  • Ketidaksesuaian seperti ini menjadi dasar bagi HTTP desync attacks atau request smuggling, ketika reverse proxy dan backend memahami batas pesan secara berbeda dan menimbulkan masalah keamanan serius
  • Pendekatan menambal perbedaan parser terus-menerus sulit menjadi solusi mendasar

Penanganan batas pesan di FastCGI dan HTTP/2

  • HTTP/2 dapat menyelesaikan masalah desync dengan memperjelas batas pesan jika digunakan secara konsisten antara proxy dan backend
  • FastCGI telah menyediakan pemisahan batas yang jelas seperti ini dengan protokol yang lebih sederhana sejak 1996
  • nginx mendukung backend FastCGI sejak rilis pertamanya, tetapi dukungan backend HTTP/2 baru ditambahkan pada paruh akhir 2025
  • Dukungan backend HTTP/2 di Apache masih berstatus "experimental"

Masalah header yang tidak tepercaya dan cara pemisahan di FastCGI

  • Bukan hanya soal desync, HTTP juga tidak punya cara yang kuat untuk membawa data yang harus dipercaya dan diteruskan proxy, seperti IP klien sebenarnya, nama pengguna yang telah diautentikasi oleh proxy, atau informasi sertifikat klien pada mTLS
  • Dalam praktiknya, informasi seperti ini biasanya dimasukkan ke header HTTP, tetapi tidak ada pemisahan struktural antara data tepercaya yang ditambahkan proxy dan header tidak tepercaya yang dikirim klien
  • Header seperti X-Real-IP sering dipakai untuk meneruskan IP klien sebenarnya, tetapi agar aman proxy harus benar-benar menghapus semua header yang sudah ada lalu menambahkannya kembali, termasuk variasi huruf besar-kecil
  • Pendekatan ini adalah medan yang sangat berbahaya, dan ada banyak jalur yang membuat backend akhirnya mempercayai data yang dimasukkan penyerang
  • Proxy harus menghapus bukan hanya X-Real-IP, tetapi header apa pun yang digunakan untuk tujuan semacam ini
  • Misalnya, middleware Chi menentukan IP asli klien dengan memeriksa True-Client-IP terlebih dahulu, dan hanya memakai X-Real-IP jika header itu tidak ada
    • Meski proxy menangani X-Real-IP dengan benar, masalah tetap bisa muncul jika penyerang mengirim True-Client-IP
  • FastCGI membedakan header klien dan informasi tambahan dari proxy dengan pendekatan pemisahan domain
    • Keduanya sama-sama dikirim sebagai daftar parameter key/value, tetapi nama header HTTP diberi awalan HTTP_
    • Karena itu, tidak terbentuk situasi di mana header yang dikirim klien dapat ditafsirkan sebagai data tepercaya milik proxy

Penanganan informasi tepercaya FastCGI di Go

  • FastCGI mendefinisikan parameter standar seperti REMOTE_ADDR untuk meneruskan IP klien sebenarnya
  • net/http/fcgi di Go secara otomatis mengisi nilai ini ke RemoteAddr pada http.Request, sehingga bekerja tanpa middleware tambahan
  • Proxy juga dapat meneruskan informasi seperti penggunaan HTTPS, TLS cipher suite yang dinegosiasikan, dan sertifikat klien sebagai parameter non-standar
  • Go secara otomatis mengatur field TLS pada Request menjadi nilai non-nil saat permintaan menggunakan HTTPS
    • Walaupun kosong, ini tetap berguna untuk memeriksa apakah HTTPS diwajibkan
  • Dengan fcgi.ProcessEnv, seluruh kumpulan parameter tepercaya yang dikirim proxy dapat diakses

Alasan adopsinya lambat dan keterbatasan praktis

  • Jika FastCGI memang lebih baik, alasan mengapa ia tidak dipakai luas tampaknya dipengaruhi oleh kesan usang dari namanya sendiri dan kurangnya kesadaran terhadap masalah keamanan reverse proxy HTTP
  • Watchfire sudah membahas serangan desync pada 2005 dan juga memperingatkan bahwa penyelesaiannya tidak mudah, tetapi serangan seperti ini tidak benar-benar mendapat perhatian selama lebih dari 10 tahun
  • FastCGI masih bisa digunakan secara nyata hingga sekarang, dan SSLMate telah memakainya di produksi selama lebih dari 10 tahun
  • Namun, sebagai teknologi lama, ia juga punya kelemahan
    • Tidak pernah diperbarui untuk mendukung WebSockets
    • Ekosistem alatnya kurang memadai
    • Misalnya, curl mendukung FTP, Gopher, bahkan SMTP, tetapi tidak bisa mengirim permintaan FastCGI
  • Saat server FastCGI Go dibenchmark di belakang beberapa reverse proxy, sebagian workload menunjukkan throughput lebih rendah dibanding HTTP/1.1 atau HTTP/2
    • Ini dipandang bukan sebagai keterbatasan protokol itu sendiri, melainkan akibat jalur kode FastCGI yang belum dioptimalkan setara dengan HTTP

Penilaian akhir

  • Jika WebSockets tidak dibutuhkan dan performa saat ini sudah memadai, FastCGI tetap merupakan pilihan yang layak digunakan
  • Bahkan jika nanti muncul bottleneck, menambah perangkat keras dinilai lebih baik daripada menerima kompleksitas dan mimpi buruk keamanan dari reverse proxy HTTP

2 komentar

 
rtyu1120 6 jam lalu

Komentar Twisted tentang FastCGI yang saya temukan di komentar Lobsters cukup mengesankan https://web.archive.org/web/20160723091923/…

 
GN⁺ 6 jam lalu
Opini Hacker News
  • Saya setuju dengan maksud tulisannya. Untuk penggunaan seperti ini, menurut saya FastCGI lebih baik daripada HTTP
    Saya juga ingin memperkenalkan protokol bernama WAS(Web Application Socket). Enam belas tahun lalu, di tempat kerja saya merasa FastCGI pun belum cukup baik, jadi saya merancangnya sendiri
    Alih-alih framing socket utama, ia memakai 1 control socket dan 2 pipe untuk body request/response mentah, dan baik aplikasi WAS maupun web server bisa memanfaatkan splice() pada pipe tersebut
    Tidak perlu framing, pembatalan request juga dimungkinkan, dan tiga file descriptor selalu bisa dipulihkan
    Selama bertahun-tahun ini dipakai di aplikasi internal dan lingkungan web hosting, dan saya juga menulis sendiri PHP SAPI-nya. Cukup banyak situs web yang secara internal berjalan di atas WAS
    Semuanya open source
    library: https://github.com/CM4all/libwas
    documentation: https://libwas.readthedocs.io/en/latest/
    non-blocking library: https://github.com/CM4all/libcommon/tree/master/src/was/asyn...
    our web server: https://github.com/CM4all/beng-proxy
    WebDAV: https://github.com/CM4all/davos
    PHP fork with WAS SAPI: https://github.com/CM4all/php-src

    • FastCGI dan HTTP bukan berada pada lapisan yang sama
      HTTP dipakai untuk pengiriman data antara dua ujung seperti browser dan server, sedangkan FastCGI dipakai untuk memproses data itu antara server dan aplikasi
      Saya baru saja membaca sekilas artikelnya, dan penulis tampaknya menulis dengan cara yang membingungkan seolah keduanya saling dapat menggantikan. Padahal sama sekali tidak begitu
      Sebagai catatan, saya juga sudah memakai fcgi selama 10 tahun untuk layanan pelanggan web
  • Artikel ini justru menarik karena banyak yang terlewat
    Saat perdebatan FastCGI vs. SCGI vs. HTTP sedang panas, saya mendirikan startup Web2.0 dan menyusun sendiri stack frontend-nya, dan pada akhirnya HTTP menang karena kesederhanaannya
    Karena HTTP memang sudah harus ditangani di gateway, memakai itu apa adanya berarti tidak perlu menambahkan protokol lain ke stack, dan ini membuat konfigurasi seperti menaruh reverse proxy di beberapa lapis atau memisahkan perhatian lintas-fungsi seperti autentikasi, sesi, terminasi SSL, dan filtering DDoS ke server per peran menjadi sangat mudah
    Di lingkungan pengembangan, kita bisa langsung terhubung ke app server lewat HTTP, dan di produksi reverse proxy yang menangani SSL, autentikasi, dan deteksi penyalahgunaan, sehingga app server yang sama bisa dipakai ulang begitu saja
    Saat itu, nginx juga jauh lebih cepat dan stabil daripada kebanyakan modul FastCGI/SCGI. Awalnya saya memakai HTTP -> Lighttpd -> FastCGI -> Django, tapi ternyata jauh lebih cepat kalau langsung pakai nginx
    Penggunaan HTTP bekerja seperti End-to-End Principle versi web. Maksudnya, jaringan dan protokol seharusnya tidak peduli pada isi yang dibawa, dan logika aplikasi seharusnya berada di ujung, bukan di node jaringan yang memfilter atau mengalihkan
    Namun, inti yang ditunjukkan artikel ini adalah bahwa dari sisi keamanan, sering kali lebih baik mengikuti prinsip least privilege. Hanya komunikasi yang sudah diperkirakan yang boleh lolos lewat allowlist agar kita tidak tanpa sadar ikut berkontribusi pada kompromi di titik lain
    Pada akhirnya ada ketegangan antara keduanya. E2E memberi fleksibilitas, tetapi fleksibilitas itu juga memperbesar peluang penyalahgunaan, sedangkan PoLP memberi keamanan, tetapi membuat sistem hanya bisa melakukan hal-hal yang sudah dirancang sehingga lebih sulit beradaptasi dengan kebutuhan baru
    [1] https://en.wikipedia.org/wiki/End-to-end_principle
    [2] https://en.wikipedia.org/wiki/Principle_of_least_privilege

    • Menurut saya analogi itu tidak terlalu cocok, terutama dalam konteks connection caching dan multiplexing
      Jika gateway perantara memultiplex banyak request HTTP ke satu kanal HTTP lain, dan kanal itu tersambung langsung ke listening service tanpa didemultiplex sebelum socket aplikasi, maka itu pada dasarnya merusak logika end-to-end dalam banyak hal
      Analogi itu baru agak masuk jika simetri koneksi 1:1 tetap dipertahankan
      Menurut saya, kerentanan reverse proxy semuanya langsung berasal dari pelanggaran terhadap end-to-end
      Jika analogi itu benar, maka pengiriman SMTP yang melewati banyak MX juga harus dianggap end-to-end, padahal kenyataannya tidak, dan ia juga menimbulkan masalah mirip reverse proxy, misalnya desinkronisasi batas pesan
      Saya paham maksud untuk memetakan request HTTP ke pesan, tetapi itu cepat runtuh karena semantik TCP·HTTP yang nyata dan berbagai detail protokol
      Prinsip end-to-end tidak membolehkan kita memperlakukan semantik secara serampangan. Ia menuntut disiplin yang sangat ketat soal manajemen state dan batas lapisan transport. Sesuatu yang kira-kira mirip end-to-end bukanlah end-to-end
    • Bagi pengembang web app, HTTP semantics itu berguna, tetapi wire protocol HTTP sendiri buruk
      Misalnya, multiplexing bahkan belum ada sebelum HTTP 2.0, jadi memakai HTTP apa adanya untuk komunikasi antara reverse proxy dan backend itu sangat boros
      Ada juga masalah keamanan. Parser yang berbeda bisa menafsirkan batas akhir request secara berbeda
      Google juga sejak lama membungkus HTTP antara front web server dan aplikasi dengan protokol internal mereka, Stubby
      Jauh lebih cepat daripada wire protocol HTTP dan fiturnya juga lebih banyak. Biasanya ini berlebihan untuk kebanyakan perusahaan, tetapi pada skala besar biaya membuat wire protocol lain beserta tooling di sekitarnya sendiri menjadi cukup layak
    • Menerapkan end-to-end principle di dalam data center tidak terlalu bermakna, dan seperti yang ditunjukkan artikel ini, justru memungkinkan perilaku yang tidak aman
    • Hal yang saya benci dari nginx adalah dokumentasi-nya. Rasanya nyaris tidak berguna
      httpd juga pada titik tertentu bergerak ke arah yang membuat konfigurasi makin sulit, dan saya meninggalkannya saat mereka tiba-tiba mengganti format konfigurasinya
      Sebenarnya saya bisa saja beradaptasi, tetapi sebagai gantinya saya pindah ke lighttpd, lalu setelah itu ruby mengotomatisasi pembuatan konfigurasi sehingga secara teknis saya sebenarnya bisa kembali ke httpd
      Meski begitu, saya tetap tidak ingin kembali. Jika Anda pengembang web server, Anda seharusnya berhati-hati terhadap keputusan yang memaksa pengguna menyesuaikan diri dengan format baru
      Kalau memang akan mengganti format konfigurasi untuk keputusan yang sebenarnya sederhana, setidaknya sediakan opsi tambahan seperti konfigurasi yaml agar pengguna tidak tiba-tiba dipaksa memakai format konfigurasi bergaya if-clause yang baru
  • Sekarang, setelah WHATWG streams tersebar luas di browser, cukup mudah mengimplementasikan sesuatu yang mirip WebSocket sendiri di atas request HTTP berumur panjang
    Cukup kirim byte stream dan tambahkan header di depan tiap pesan; dalam banyak kasus satu nilai panjang saja sudah cukup
    Ada juga keuntungannya. Tidak seperti WebSocket, ini tidak memerlukan jalur khusus terpisah di lapisan server, bisa memakai backpressure, mendapat peningkatan HTTP/2·HTTP/3 secara gratis, dan overhead framing-nya juga lebih rendah
    Namun AFAIK masih belum didukung untuk terus men-streaming body request sambil sekaligus menerima response, jadi untuk streaming dua arah penuh tetap butuh dua request

  • Saya baru menemukan kembali plain CGI yang lama, dan ternyata sangat cocok untuk membiarkan pengguna melakukan vibe code pada halaman kustom di platform kami [1]
    Fitur bawaan kami mencakup task list dan data viewer, tetapi pengguna sering menginginkan kustomisasi yang jauh lebih rinci, seperti tampilan Kanban atau dashboard kustom dengan filter data dan chart
    Di kotak ini ada coding agent, jadi alih-alih kami membuat report builder tradisional, pengguna bisa langsung membuat sendiri apa yang mereka inginkan dalam bentuk kode
    Go stdlib punya dukungan yang bagus baik di sisi server maupun user space, dan jika coding agent membuat page-name/main.go lalu berkomunikasi lewat CGI, server tinggal mendelegasikan request ke sana
    Karena skala data dan pageview semuanya masih person scale, optimasi seperti FastCGI sama sekali tidak terlalu dibutuhkan
    Di era agent, teknologi lama terasa baru lagi

    1. https://housecat.com
    • Berbeda dari FastCGI, CGI meneruskan header HTTP sebagai environment variable, jadi hati-hati karena ini punya jebakan yang cukup besar: https://httpoxy.org/
      Implementasi server CGI di Go aman pada bagian itu karena tidak menetapkan $HTTP_PROXY, tetapi saya tetap tidak suka dengan cara CGI memakai environment variable
  • Di sisi reverse proxy, kebanyakan pekerjaannya sederhana, jadi memakai fitur bawaan Nginx biasanya sudah cukup
    Meski begitu, jika butuh sesuatu yang lebih kompleks, ide untuk memakai FastCGI sepertinya tidak akan terpikir oleh saya
    Sekitar 10 tahun lalu saya sempat sedikit memakai FastCGI untuk menjalankan sebagian kode C++ di web, tetapi setelah itu hampir tidak pernah lagi

    • Sekarang embedded server jauh lebih umum
      Cukup tanamkan langsung HTTP server di dalam aplikasi, lalu lakukan sendiri apa yang diperlukan tanpa gateway
  • Konfigurasi PHP/Apache yang didistribusikan di keluarga Red Hat adalah FPM(FastCGI Process Manager)
    Saya tidak tahu apakah FastCGI juga dipakai di tempat lain di distribusi RHEL
    $ rpm -qi php-fpm | grep ^Summary
    Summary : PHP FastCGI Process Manager

  • Ada juga uwsgi protocol
    Ini pada dasarnya juga mirip RPC untuk hampir semua hal

  • FCGI juga merupakan sistem orkestrasi
    Saat beban naik, ia menyalakan lebih banyak task server, saat beban turun ia mematikannya, dan jika task mati ia menyalakan salinan baru
    Semacam Kubernetes untuk satu sistem

    • Dalam pengalaman saya, fitur itu tidak terlalu bagus
      Kedengarannya memang bagus, tetapi sering terjadi sistem baik-baik saja saat beban rendah, lalu kehabisan memori ketika beban tinggi datang karena ia mulai membuat lebih banyak worker
      Karena itu, biasanya lebih baik memakai jumlah worker statis
      Namun crash recovery tetap berguna bila memang dibutuhkan
    • Kami juga memakainya persis seperti itu
  • Cukup luangkan sejenak untuk mengagumi keabsurdan header HTTP
    Jika kita hanya memakai X-Real-IP saat True-Client-IP tidak ada, maka bahkan jika proxy sudah mengisi X-Real-IP dengan benar, penyerang tetap bisa lolos hanya dengan mengirim header True-Client-IP
    Ada X-Forwarded-For, X-Real-IP, sampai header kustom yang berbeda-beda untuk tiap CDN; sebagian berupa daftar yang dipisahkan koma dan biasanya bahkan ikut menambahkan IP LB kita sendiri yang tidak ada gunanya
    Saya tahu kenapa bisa begitu, tetapi itu sama sekali tidak membantu
    Lebih buruk lagi, semua header ini juga bisa disisipkan oleh user-agent yang berniat jahat. Rasanya seperti tidak pernah ada kesepakatan tentang bagaimana server tepercaya seharusnya meneruskan informasi penting di sepanjang pipeline
    Kekacauan ini juga sangat cocok dipasangkan dengan keabsurdan header User-Agent
    Di situ Apple mendorong semuanya lebih jauh lagi dengan dalih privasi, sampai mengirim informasi palsu total, misalnya versi OS bohong dan omong kosong serupa

  • Ada banyak benarnya dalam argumen ini, tetapi FastCGI mewarisi kehilangan informasi dari CGI/1.1 pada bagian seperti PATH_INFO
    URL decoding dipaksakan sehingga encoded slash seperti %2F tidak bisa direpresentasikan
    Tergantung implementasinya, // di path kadang juga digabung menjadi /, walau ini juga masalah yang ada di banyak implementasi HTTP
    Dari sisi daya ekspresi, ini lebih lemah daripada HTTP, dan apakah perbedaan itu penting atau tidak tergantung aplikasinya
    Saya pribadi lebih suka penanganan URL yang presisi