- 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
Komentar Twisted tentang FastCGI yang saya temukan di komentar Lobsters cukup mengesankan https://web.archive.org/web/20160723091923/…
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 tersebutTidak 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
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 nginxPenggunaan 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
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
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
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.golalu berkomunikasi lewat CGI, server tinggal mendelegasikan request ke sanaKarena skala data dan pageview semuanya masih person scale, optimasi seperti FastCGI sama sekali tidak terlalu dibutuhkan
Di era agent, teknologi lama terasa baru lagi
Implementasi server CGI di Go aman pada bagian itu karena tidak menetapkan
$HTTP_PROXY, tetapi saya tetap tidak suka dengan cara CGI memakai environment variableDi 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
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 ^SummarySummary : PHP FastCGI Process ManagerItu termasuk di paket
httpd-coreFedora. Untuk RHEL saya kurang tahu: https://packages.fedoraproject.org/pkgs/httpd/httpd-core/fed...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
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
Cukup luangkan sejenak untuk mengagumi keabsurdan header HTTP
Jika kita hanya memakai
X-Real-IPsaatTrue-Client-IPtidak ada, maka bahkan jika proxy sudah mengisiX-Real-IPdengan benar, penyerang tetap bisa lolos hanya dengan mengirim headerTrue-Client-IPAda
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 gunanyaSaya 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_INFOURL decoding dipaksakan sehingga encoded slash seperti
%2Ftidak bisa direpresentasikanTergantung implementasinya,
//di path kadang juga digabung menjadi/, walau ini juga masalah yang ada di banyak implementasi HTTPDari 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