- लंबे समय तक चलने वाले बैकएंड को socket के जरिए requests भेजने वाले proxy protocol के रूप में, इसे मौजूदा HTTP handler structure में लगभग बिना बदलाव के लागू किया जा सकता है
- HTTP/1.1 reverse proxy में message boundary parsing अलग-अलग implementation में आसानी से भिन्न हो सकती है, जिससे desync और request smuggling जैसी गंभीर security समस्याएँ लगातार पैदा हो सकती हैं
- FastCGI 1996 से स्पष्ट message framing देता आ रहा है, और client headers तथा proxy द्वारा जोड़ी गई trusted जानकारी को संरचनात्मक रूप से अलग करता है
- Go का
net/http/fcgiREMOTE_ADDRकोRequest.RemoteAddrमें भरता है और HTTPS स्थिति कोRequest.TLSमें भी दर्शाता है, इसलिए trusted information forwarding को अलग middleware के बिना संभाला जा सकता है - WebSockets का समर्थन न होना, कमजोर tooling ecosystem, और कुछ workloads में कम throughput जैसी सीमाएँ हैं, लेकिन अगर WebSockets की ज़रूरत नहीं है और performance पर्याप्त है, तो यह अब भी एक व्यावहारिक विकल्प दिखता है
FastCGI की स्थिति और इसे अपनाने का तरीका
- FastCGI सिर्फ file-by-file process execution के लिए नहीं है; इसे लंबे समय तक चलने वाले daemon को TCP या UNIX socket के जरिए requests भेजने वाले proxy-backend protocol के रूप में भी इस्तेमाल किया जा सकता है
- Go में
net/http/fcgipackage import करकेhttp.Serveकोfcgi.Serveसे बदलने भर से इसे अपनाया जा सकता है- मौजूदा handlers पहले की तरह
http.ResponseWriterऔरhttp.Requestका उपयोग करते हैं - application की बाकी structure भी जस की तस रहती है
- मौजूदा handlers पहले की तरह
- Apache, Caddy, nginx, HAProxy जैसे प्रमुख proxies FastCGI backend को support करते हैं और configuration भी अपेक्षाकृत सरल है
बैकएंड प्रोटोकॉल के रूप में HTTP इस्तेमाल करने पर parsing समस्याएँ
- HTTP reverse proxying लगभग एक security minefield है, और Discord media proxy की desync vulnerability जैसी समस्याएँ, जिनसे निजी attachments तक झाँका जा सकता है, लगातार सामने आती रहती हैं
- HTTP/1.1 ऊपर से देखने पर सरल text protocol लगता है, लेकिन एक ही message को व्यक्त करने के बहुत अधिक तरीके हैं और exception handling भी बहुत है, इसलिए implementation के बीच interpretation बदलना आसान है
- सबसे बड़ी समस्या यह है कि HTTP messages में explicit framing नहीं होता
- message का अंत स्वयं message कई तरीकों से बताता है
- अलग-अलग implementations message के समाप्त होने और अगले message के शुरू होने की जगह को अलग तरह से समझ सकती हैं
- यही असंगति HTTP desync attacks या request smuggling की नींव बनती है, जहाँ reverse proxy और backend message boundary को अलग तरह से समझते हैं और गंभीर security समस्याएँ पैदा होती हैं
- parser differences को लगातार patch करना मूल समाधान बनना मुश्किल है
- James Kettle लगातार नए प्रकार खोजते रहे हैं
- पिछले साल अतिरिक्त मामलों को खोजने के बाद उन्होंने "HTTP/1.1 must die" जैसी अभिव्यक्ति तक इस्तेमाल की
FastCGI और HTTP/2 में message boundary handling
- HTTP/2 अगर proxy और backend के बीच लगातार एक समान तरीके से इस्तेमाल किया जाए, तो यह message boundaries को स्पष्ट करके desync समस्या को हल कर सकता है
- FastCGI 1996 से ही यह स्पष्ट boundary separation एक और भी सरल protocol के साथ देता आ रहा है
- nginx ने अपनी पहली release से ही FastCGI backend को support किया, लेकिन HTTP/2 backend support 2025 के उत्तरार्ध में जाकर जोड़ा गया
- Apache का HTTP/2 backend support अब भी "experimental" स्थिति में है
अविश्वसनीय headers की समस्या और FastCGI का separation मॉडल
- समस्या सिर्फ desync तक सीमित नहीं है; HTTP में वास्तविक client IP, proxy द्वारा संसाधित authenticated username, या mTLS में client certificate जानकारी जैसे data को, जिसे proxy को भरोसे के साथ आगे भेजना चाहिए, मज़बूती से ले जाने का तरीका भी कमजोर है
- व्यवहार में यह जानकारी HTTP headers में डाल दी जाती है, लेकिन proxy द्वारा जोड़ा गया trusted data और client द्वारा भेजे गए untrusted headers के बीच कोई संरचनात्मक विभाजन नहीं होता
X-Real-IPजैसे headers वास्तविक client IP पहुँचाने के लिए अक्सर इस्तेमाल होते हैं, लेकिन यह तभी सुरक्षित होता है जब proxy पहले case variations सहित सभी मौजूदा headers को पूरी तरह हटाकर फिर से जोड़ दे- यह तरीका बहुत जोखिमभरा क्षेत्र है, और backend के attacker द्वारा डाले गए data पर भरोसा कर लेने के कई रास्ते बनते हैं
- proxy को सिर्फ
X-Real-IPही नहीं, बल्कि इस उद्देश्य के लिए इस्तेमाल होने वाले किसी भी header को पूरी तरह हटाना चाहिए - उदाहरण के लिए Chi middleware client की वास्तविक IP तय करते समय पहले
True-Client-IPको जाँचता है, और उसके न होने पर हीX-Real-IPका उपयोग करता है- proxy भले
X-Real-IPको सही तरह संभाले, attacker अगरTrue-Client-IPभेज दे तो समस्या हो सकती है
- proxy भले
- FastCGI client headers और proxy द्वारा जोड़ी गई जानकारी को domain separation के जरिए अलग करता है
- दोनों ही key/value parameters की सूची के रूप में भेजे जाते हैं, लेकिन HTTP header names के साथ
HTTP_prefix जोड़ा जाता है - इसलिए client द्वारा भेजा गया header proxy के trusted data के रूप में interpret होने की संरचना बन ही नहीं पाती
- दोनों ही key/value parameters की सूची के रूप में भेजे जाते हैं, लेकिन HTTP header names के साथ
Go में FastCGI trusted information handling
- FastCGI वास्तविक client IP पहुँचाने के लिए
REMOTE_ADDRजैसे standard parameters परिभाषित करता है - Go का
net/http/fcgiइस value को अपने आपhttp.RequestकेRemoteAddrमें भर देता है, इसलिए अलग middleware की ज़रूरत नहीं पड़ती - proxy HTTPS उपयोग, negotiated TLS cipher suite, और client certificate जैसी जानकारी non-standard parameters के रूप में भी भेज सकता है
- Go, अगर request ने HTTPS इस्तेमाल किया हो, तो
RequestकेTLSfield को अपने आप non-nil value पर सेट करता है- यह खाली होने पर भी HTTPS enforcement जाँचने में उपयोगी है
fcgi.ProcessEnvके जरिए proxy द्वारा भेजे गए पूरे trusted parameter set तक पहुँचा जा सकता है
अपनाने में देरी के कारण और व्यावहारिक सीमाएँ
- अगर FastCGI बेहतर है तो यह व्यापक रूप से क्यों नहीं अपनाया गया, इस पर लेखक का मानना है कि नाम से आने वाला पुरानापन और HTTP reverse proxy security समस्याओं के प्रति जागरूकता की कमी, दोनों ने भूमिका निभाई है
- Watchfire ने 2005 में ही desync attacks पर चर्चा की थी और यह चेतावनी भी दी थी कि इसका समाधान आसान नहीं होगा, लेकिन ऐसे attacks को 10 साल से अधिक समय तक ठीक से गंभीरता नहीं मिली
- FastCGI आज भी वास्तविक उपयोग के लिए सक्षम है, और SSLMate में इसे 10 साल से अधिक समय से production में इस्तेमाल किया जा रहा है
- लेकिन पुरानी तकनीक होने के कारण इसकी कुछ कमज़ोरियाँ भी हैं
- WebSockets support के लिए इसे update नहीं किया गया
- tooling ecosystem सीमित है
- उदाहरण के लिए curl FTP, Gopher, SMTP तक support करता है, लेकिन FastCGI requests नहीं भेज सकता
- कई reverse proxies के पीछे Go FastCGI server का benchmark करने पर, कुछ workloads में HTTP/1.1 या HTTP/2 से कम throughput मिला
- लेखक इसे protocol की मूल सीमा से अधिक FastCGI code path के HTTP जितना optimized न होने का परिणाम मानते हैं
अंतिम निष्कर्ष
- अगर WebSockets की ज़रूरत नहीं है और मौजूदा performance पर्याप्त है, तो FastCGI अब भी एक उपयोगी विकल्प है
- लेखक का मानना है कि bottleneck आने पर भी HTTP reverse proxying की complexity और security nightmare झेलने के बजाय और hardware जोड़ना बेहतर विकल्प है
2 टिप्पणियां
Lobsters की टिप्पणियों में मिला Twisted का FastCGI पर कमेंट काफ़ी प्रभावशाली है https://web.archive.org/web/20160723091923/…
Hacker News की राय
मैं लेख की मूल बात से सहमत हूँ। इस तरह के उपयोग में FastCGI मुझे HTTP से बेहतर लगता है
मैं WAS(Web Application Socket) नाम का एक प्रोटोकॉल भी बताना चाहता हूँ। 16 साल पहले नौकरी के दौरान मुझे लगा कि FastCGI भी पर्याप्त अच्छा नहीं है, इसलिए मैंने इसे खुद डिज़ाइन किया था
मुख्य socket framing की जगह इसमें 1 control socket और raw request/response body के लिए 2 pipe इस्तेमाल होते हैं, और WAS app व web server दोनों pipe पर
splice()का उपयोग कर सकते हैंframing की ज़रूरत नहीं पड़ती, request cancel भी किया जा सकता है, और तीनों file descriptor हमेशा recover किए जा सकते हैं
इसे मैंने कई सालों तक internal applications और web hosting environment में इस्तेमाल किया है, और PHP SAPI भी खुद लिखा है। काफ़ी सारी websites अंदरूनी तौर पर WAS पर चलती हैं
सब कुछ 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 का काम browser और server जैसे दो endpoints के बीच data transfer करना है, जबकि FastCGI server और application के बीच उस data को process करने के लिए है
मैंने अभी लेख को जल्दी से देखा, और लगा कि लेखक दोनों को ऐसे लिख रहा है मानो वे एक-दूसरे के विकल्प हों। असल में ऐसा बिल्कुल नहीं है
वैसे, मैं भी web customer services में fcgi को 10 साल तक इस्तेमाल कर चुका हूँ
यह लेख काफ़ी कुछ छोड़ देता है, इसलिए उल्टा और दिलचस्प लगता है
जब FastCGI vs. SCGI vs. HTTP पर बहस चल रही थी, तब मैंने एक Web2.0 startup शुरू किया था और frontend stack खुद बनाया था। आख़िर में HTTP इसलिए जीता क्योंकि वह simple था
gateway पर जो HTTP वैसे भी संभालना ही था, उसी को वैसे का वैसा इस्तेमाल करने से stack में कोई और protocol जोड़ने की ज़रूरत नहीं पड़ी। इससे कई स्तरों वाले reverse proxy लगाना या authentication, session, SSL termination, DDoS filtering जैसी cross-cutting concerns को अलग-अलग servers में बाँटना बहुत आसान हो गया
development environment में app server से सीधे HTTP पर जुड़ सकते थे, और production में SSL, authentication, abuse detection reverse proxy संभाल लेता था। इस तरह वही app server ज्यों का त्यों reuse हो जाता था
उस समय nginx ज़्यादातर FastCGI/SCGI modules से कहीं ज़्यादा तेज़ और stable था, यह भी बड़ा कारण था। शुरुआत में सेटअप
HTTP -> Lighttpd -> FastCGI -> Djangoथा, लेकिन सिर्फ़ nginx इस्तेमाल करना काफ़ी तेज़ निकलाHTTP का उपयोग web वाले End-to-End Principle जैसा काम करता था। यानी network और protocol को इस बात से स्वतंत्र होना चाहिए कि भीतर क्या जा रहा है, और application logic filtering/redirecting करने वाले network nodes में नहीं बल्कि endpoints पर होना चाहिए
लेकिन लेख जिस मुख्य बात की ओर इशारा करता है, वह यह है कि security के लिहाज़ से least privilege principle का पालन करना कई बार बेहतर होता है। allowlist के ज़रिए सिर्फ़ वही communication गुजरना चाहिए जिसकी उम्मीद हो, ताकि कहीं और हुए compromise में अनजाने में योगदान न हो
आख़िरकार इन दोनों के बीच तनाव है। E2E flexibility देता है, लेकिन वही flexibility abuse की गुंजाइश भी बढ़ाती है; PoLP security देता है, लेकिन फिर सिस्टम सिर्फ़ वही कर सकता है जिसके लिए उसे डिज़ाइन किया गया हो, इसलिए नई requirements के साथ ढलना कठिन हो जाता है
[1] https://en.wikipedia.org/wiki/End-to-end_principle
[2] https://en.wikipedia.org/wiki/Principle_of_least_privilege
अगर कोई बीच का gateway कई HTTP requests को किसी दूसरे एक HTTP channel पर multiplex करे, और वह channel listening service तक सीधे जाए तथा application socket से पहले demultiplex न हो, तो वह कई मायनों में end-to-end logic को बुनियादी तौर पर तोड़ देता है
वह उपमा तभी कुछ हद तक चलती है जब 1:1 connection symmetry बनी रहे
मेरे हिसाब से reverse proxy की vulnerabilities सीधे-सीधे end-to-end तोड़ने से पैदा हुई हैं
अगर वह उपमा सही हो, तो कई MX से होकर जाने वाला SMTP delivery भी end-to-end होना चाहिए, जबकि असल में ऐसा नहीं है, और उसमें reverse proxy जैसे ही कई मसले आते हैं, जैसे message boundary desync
मैं समझता हूँ कि HTTP requests को messages से मिलाने की कोशिश की जा रही है, लेकिन असली TCP·HTTP semantics और ढेर सारी protocol details के कारण यह बहुत जल्दी टूट जाती है
end-to-end principle semantics के साथ ढीला व्यवहार करने की अनुमति नहीं देता। यह state management और transport-layer boundaries के बारे में बहुत सख़्त अनुशासन मांगता है। लगभग end-to-end जैसा कुछ end-to-end नहीं होता
उदाहरण के लिए multiplexing भी HTTP 2.0 से पहले नहीं था, इसलिए reverse proxy और backend के बीच HTTP को ज्यों का त्यों इस्तेमाल करना काफ़ी wasteful है
security problems भी हैं। अलग-अलग parsers request boundaries के ख़त्म होने की जगह भी अलग तरह से समझ सकते हैं
Google भी बहुत पहले से frontend web server और application के बीच HTTP को अपने Stubby protocol में लपेटकर इस्तेमाल करता है
वह HTTP wire protocol से कहीं तेज़ है और features भी ज़्यादा देता है। आम कंपनियों के लिए यह ज़रूरत से ज़्यादा हो सकता है, लेकिन scale बढ़ने पर दूसरा wire protocol और उसके आसपास की tooling खुद बनाने की लागत पूरी तरह जायज़ हो जाती है
httpd भी किसी मोड़ पर configuration को ज़रूरत से ज़्यादा कठिन बनाने की दिशा में चला गया, और जिस समय उसने config format अचानक बदल दिया, मैंने उसे छोड़ दिया
मैं चाहूँ तो उसके हिसाब से ढल सकता था, लेकिन उसकी जगह lighttpd पर चला गया। बाद में ruby ने configuration generation automate कर दिया, इसलिए तकनीकी रूप से मैं फिर httpd पर लौट भी सकता हूँ
फिर भी लौटना नहीं चाहता। अगर आप web server developer हैं, तो users को ज़बरदस्ती नए format के मुताबिक ढालने वाली चीज़ों के बारे में सावधान रहना चाहिए
अगर किसी बहुत साधारण फ़ैसले से config format बदलना ही है, तो कम-से-कम yaml configuration जैसी कोई optional सुविधा देनी चाहिए, ताकि अचानक नया if-clause style config लिखने पर मजबूर न किया जाए
अब जब WHATWG streams browser में काफ़ी फैल चुके हैं, तो लंबे समय तक चलने वाले HTTP requests के ऊपर अपना WebSocket-जैसा कुछ बनाना काफ़ी आसान है
बस byte stream भेजिए और हर message के आगे एक header लगा दीजिए; कई मामलों में सिर्फ़ length value ही काफ़ी होती है
इसके कुछ फायदे भी हैं। WebSocket की तरह server layer में अलग special path नहीं चाहिए, backpressure इस्तेमाल कर सकते हैं, HTTP/2·HTTP/3 के improvements मुफ़्त में मिल जाते हैं, और framing overhead भी कम होता है
लेकिन AFAIK request body को लगातार stream करते हुए एक साथ response पाना अभी supported नहीं है, इसलिए पूरी bidirectional streaming के लिए दो requests चाहिए
मैंने पुराना plain CGI फिर से खोजा, और हमारे platform पर users को custom pages vibe code कराने के लिए यह बहुत बढ़िया निकला [1]
built-in features के तौर पर task list और data viewer हैं, लेकिन users अक्सर Kanban view या data filter·chart वाले custom dashboard जैसी कहीं ज़्यादा बारीक customization चाहते हैं
इस box में एक coding agent है, इसलिए traditional report builder बनाने की बजाय हम users को उनकी ज़रूरत की चीज़ खुद code करने दे सकते हैं
Go stdlib में server side और user space दोनों तरफ़ अच्छा support है, और अगर coding agent
page-name/main.goबनाकर CGI से बात करे, तो server request वहाँ delegate कर देता हैdata volume और pageviews सब person scale पर हैं, इसलिए FastCGI जैसी optimization की कोई ख़ास ज़रूरत भी नहीं पड़ती
agents के दौर में पुरानी technologies फिर नई लगने लगती हैं
Go की CGI server implementation
$HTTP_PROXYसेट नहीं करती, इसलिए वह हिस्सा safe है, लेकिन फिर भी CGI का environment variables इस्तेमाल करने का तरीका मुझे पसंद नहीं हैreverse proxy वाली तरफ़ ज़्यादातर काम simple ही होते थे, इसलिए सिर्फ़ Nginx की built-in functionality से काम चल जाता था
फिर भी जब कभी ज़्यादा complex चीज़ चाहिए होती, तब FastCGI इस्तेमाल करने का विचार मेरे मन में शायद नहीं आता
करीब 10 साल पहले web पर कुछ C++ code चलाने के लिए FastCGI थोड़ा इस्तेमाल किया था, लेकिन उसके बाद से लगभग नहीं किया
application के अंदर ही HTTP server डाल दीजिए, और gateway के बिना जो चाहिए वह सीधे कर लीजिए
Red Hat family में वितरित PHP/Apache setup में FPM(FastCGI Process Manager) होता है
मुझे नहीं पता RHEL distributions में FastCGI और कहाँ इस्तेमाल होता है
$ rpm -qi php-fpm | grep ^SummarySummary : PHP FastCGI Process ManagerFedora के
httpd-corepackage में यह शामिल है। RHEL के बारे में पक्का नहीं: https://packages.fedoraproject.org/pkgs/httpd/httpd-core/fed...uwsgi protocol भी है
यह भी व्यवहार में लगभग हर चीज़ के लिए एक RPC जैसा ही है
FCGI एक orchestration system भी है
load बढ़े तो यह server tasks और चलाता है, load घटे तो उन्हें कम करता है, और task मर जाए तो नई copy शुरू कर देता है
एक तरह का single-system Kubernetes है
सुनने में अच्छी लगती है, लेकिन अक्सर होता यह है कि सामान्य low load पर सब ठीक चलता है, और high load आते ही ज़्यादा workers बनाते-बनाते memory ख़त्म हो जाती है
इसलिए static worker count रखना आम तौर पर बेहतर रहता था
हाँ, अगर ज़रूरत हो तो crash recovery उपयोगी है
HTTP headers की बेतुकापन पर थोड़ी देर हैरान हुआ जा सकता है
अगर
True-Client-IPन होने पर हीX-Real-IPइस्तेमाल किया जाए, तो भले proxyX-Real-IPसही से सेट करे, attacker बसTrue-Client-IPheader भेजकर आपको फँसा सकता हैX-Forwarded-For,X-Real-IP, और हर CDN के अलग-अलग custom headers तक मौजूद हैं। कुछ comma-separated lists होते हैं, और उनमें अक्सर हमारे अपने LB का IP भी बेवजह जुड़ जाता हैमुझे पता है यह ऐसा क्यों है, लेकिन इससे कोई मदद नहीं मिलती
ऊपर से इन सब headers को कोई malicious user-agent inject भी कर सकता है। लगता है trusted servers के बीच pipeline में important information कैसे pass की जाए, इस पर कभी सहमति बनी ही नहीं
यह अव्यवस्था User-Agent header की बेतुकापन के साथ भी खूब मेल खाती है
वहाँ तो Apple ने privacy के नाम पर पूरी तरह fake information भेजने, जैसे झूठा OS version बताने, जैसी और भी चरम बात कर दी है
इस दावे में काफ़ी दम है, लेकिन FastCGI
PATH_INFOजैसी चीज़ों में CGI/1.1 का पालन करता है, इसलिए उसमें कुछ loss होता हैURL decoding अनिवार्य होने से encoded slash
%2Fको व्यक्त नहीं किया जा सकताimplementation के हिसाब से path में
//को/में मिला भी दिया जाता है, हालाँकि यह कई HTTP implementations में भी समस्या हैexpressiveness के लिहाज़ से यह HTTP से कमज़ोर है, और यह फ़र्क़ कितना मायने रखता है, यह application पर निर्भर करेगा
मुझे URL को बिल्कुल सटीक रूप में संभालना ज़्यादा पसंद है