1 คะแนน โดย GN⁺ 4 시간 전 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • ในฐานะโปรโตคอลพร็อกซีที่ส่งต่อคำขอไปยัง แบ็กเอนด์ที่รันระยะยาว ผ่านซ็อกเก็ต สามารถนำมาใช้ได้โดยแทบไม่ต้องเปลี่ยนโครงสร้าง HTTP handler เดิม
  • รีเวิร์สพร็อกซี HTTP/1.1 มีแนวโน้มตีความขอบเขตของข้อความไม่ตรงกันในแต่ละ implementation จึงก่อปัญหาความปลอดภัยร้ายแรงอย่าง desync และ request smuggling ได้อย่างต่อเนื่อง
  • FastCGI ให้ message framing ที่ชัดเจนมาตั้งแต่ปี 1996 และแยกส่วน header ของไคลเอนต์ออกจากข้อมูลที่เชื่อถือได้ซึ่งพร็อกซีเติมเข้ามาอย่างเป็นโครงสร้าง
  • net/http/fcgi ของ Go เติม REMOTE_ADDR ลงใน Request.RemoteAddr และสะท้อนสถานะ HTTPS ไปยัง Request.TLS ทำให้ การส่งต่อข้อมูลที่เชื่อถือได้ จัดการได้โดยไม่ต้องมีมิดเดิลแวร์แยก
  • แม้จะมีข้อจำกัดอย่างไม่รองรับ WebSockets, ecosystem เครื่องมือที่อ่อนกว่า, และ throughput ต่ำกว่า ในบาง workload แต่ถ้าไม่ต้องใช้ WebSockets และประสิทธิภาพเพียงพอ ก็ยังดูเป็นตัวเลือกที่ใช้งานได้จริง

บทบาทและวิธีนำ FastCGI ไปใช้

  • FastCGI ไม่ได้ใช้แค่กับรูปแบบรันโปรเซสแยกตามไฟล์เท่านั้น แต่ยังใช้เป็นโปรโตคอลระหว่างพร็อกซีกับแบ็กเอนด์ ที่ส่งคำขอไปยัง เดมอนที่รันระยะยาว ผ่าน TCP หรือ UNIX socket ได้ด้วย
  • ใน Go สามารถนำมาใช้ได้เพียง import แพ็กเกจ net/http/fcgi และเปลี่ยน http.Serve เป็น fcgi.Serve
    • handler เดิมยังคงใช้ http.ResponseWriter และ http.Request ได้เหมือนเดิม
    • โครงสร้างส่วนอื่นของแอปพลิเคชันก็ยังคงเดิม
  • พร็อกซีหลักอย่าง Apache, Caddy, nginx, และ HAProxy รองรับแบ็กเอนด์แบบ FastCGI และการตั้งค่าก็ค่อนข้างง่าย

ปัญหาการพาร์สเมื่อใช้ HTTP เป็นโปรโตคอลแบ็กเอนด์

  • การทำ reverse proxy ด้วย HTTP แทบจะเป็น ทุ่งระเบิดด้านความปลอดภัย และยังมีปัญหาโผล่มาเรื่อย ๆ เช่น ช่องโหว่ desync ใน media proxy ของ Discord ที่ทำให้แอบดูไฟล์แนบส่วนตัวได้
  • HTTP/1.1 แม้ดูเหมือนเป็น text protocol ที่เรียบง่าย แต่มีวิธีแทนข้อความเดียวกันมากเกินไปและมีกรณียกเว้นจำนวนมาก จึงทำให้แต่ละ implementation ตีความไม่เหมือนกันได้ง่าย
  • ปัญหาใหญ่ที่สุดคือ HTTP message ไม่มี framing ที่ชัดเจนโดยตรง
    • จุดสิ้นสุดของข้อความถูกอธิบายโดยตัวข้อความเองได้หลายวิธี
    • implementation แต่ละตัวอาจตีความจุดจบของข้อความและจุดเริ่มต้นของข้อความถัดไปต่างกัน
  • ความไม่ตรงกันนี้เป็นพื้นฐานของ HTTP desync attacks หรือ request smuggling โดยรีเวิร์สพร็อกซีและแบ็กเอนด์เข้าใจขอบเขตข้อความไม่เหมือนกัน จนกลายเป็นปัญหาความปลอดภัยร้ายแรง
  • การไล่แพตช์ความต่างของ parser ไปเรื่อย ๆ ยากจะเป็น ทางแก้ระดับรากฐาน
    • James Kettle ยังคงค้นพบรูปแบบใหม่ ๆ อย่างต่อเนื่อง
    • หลังพบกรณีเพิ่มเติมเมื่อปีก่อน เขาถึงกับใช้คำว่า "HTTP/1.1 must die"

FastCGI และ HTTP/2 กับการจัดการขอบเขตข้อความ

  • HTTP/2 หากใช้ให้สอดคล้องกันระหว่างพร็อกซีกับแบ็กเอนด์ ก็สามารถทำให้ขอบเขตข้อความชัดเจนและแก้ปัญหา desync ได้
  • FastCGI มอบการแยกขอบเขตที่ชัดเจนแบบนี้ด้วยโปรโตคอลที่ง่ายกว่ามาตั้งแต่ปี 1996
  • nginx รองรับแบ็กเอนด์ FastCGI มาตั้งแต่รุ่นแรก แต่การรองรับ แบ็กเอนด์ HTTP/2 เพิ่งถูกเพิ่มเข้ามาในช่วงปลายปี 2025
  • การรองรับแบ็กเอนด์ HTTP/2 ของ Apache ยังอยู่ในสถานะ "experimental"

ปัญหา header ที่ไม่น่าเชื่อถือและวิธีแยกของ FastCGI

  • ไม่ใช่แค่เรื่อง desync เท่านั้น HTTP ยังขาดวิธีที่แข็งแรงในการขนส่งข้อมูลที่พร็อกซีต้องเชื่อถือและส่งต่อ เช่น IP จริงของไคลเอนต์, ชื่อผู้ใช้ที่ผ่านการยืนยันตัวตนโดยพร็อกซี, หรือข้อมูลใบรับรองไคลเอนต์ใน mTLS
  • ในทางปฏิบัติ ข้อมูลเหล่านี้มักถูกใส่ลงใน HTTP header แต่ไม่มีการแบ่งแยกเชิงโครงสร้างระหว่าง ข้อมูลที่เชื่อถือได้ ที่พร็อกซีเติมเข้ามา กับ header ที่ไม่น่าเชื่อถือ ที่ไคลเอนต์ส่งมาเอง
  • header อย่าง X-Real-IP มักถูกใช้เพื่อส่งต่อ IP จริงของไคลเอนต์ แต่จะปลอดภัยก็ต่อเมื่อพร็อกซีลบ header เดิมทั้งหมดที่เกี่ยวข้องออกให้หมดก่อน แล้วจึงใส่กลับเข้าไปใหม่ รวมถึงรูปแบบตัวพิมพ์ใหญ่เล็กที่แตกต่างกันด้วย
  • วิธีนี้เป็น พื้นที่ที่อันตรายมาก และมีหลายช่องทางที่ทำให้แบ็กเอนด์เผลอเชื่อข้อมูลที่ผู้โจมตีใส่มา
  • พร็อกซีต้องลบไม่ใช่แค่ X-Real-IP แต่รวมถึง header ทุกตัว ที่ใช้เพื่อจุดประสงค์แบบนี้
  • ตัวอย่างเช่นมิดเดิลแวร์ของ Chi จะกำหนด IP จริงของไคลเอนต์โดยTrue-Client-IP ก่อน และใช้ X-Real-IP ก็ต่อเมื่อไม่มีค่าแรก
    • ต่อให้พร็อกซีจัดการ X-Real-IP ได้ถูกต้อง ถ้าผู้โจมตีส่ง True-Client-IP มาก็ยังเกิดปัญหาได้
  • FastCGI แยก header ของไคลเอนต์ออกจากข้อมูลที่พร็อกซีเติมเข้ามาโดยใช้แนวทาง domain separation
    • ทั้งสองอย่างถูกส่งในรูปแบบรายการพารามิเตอร์คีย์/ค่าเหมือนกัน แต่ชื่อ HTTP header จะมีคำนำหน้า HTTP_
    • ดังนั้นจึงไม่เกิดโครงสร้างที่ทำให้ header จากไคลเอนต์ถูกตีความเป็นข้อมูลที่เชื่อถือได้ของพร็อกซี

การจัดการข้อมูลที่เชื่อถือได้ของ FastCGI ใน Go

  • FastCGI กำหนด พารามิเตอร์มาตรฐาน อย่าง REMOTE_ADDR สำหรับส่งต่อ IP จริงของไคลเอนต์
  • net/http/fcgi ของ Go จะเติมค่านี้ลงใน http.Request ที่ RemoteAddr ให้อัตโนมัติ จึงทำงานได้โดยไม่ต้องมีมิดเดิลแวร์เพิ่ม
  • พร็อกซียังสามารถส่งข้อมูลอย่างการใช้ HTTPS, TLS cipher suite ที่เจรจาได้, และใบรับรองไคลเอนต์ ผ่าน พารามิเตอร์นอกมาตรฐาน ได้ด้วย
  • Go จะตั้งค่า field TLS ของ Request ให้ไม่เป็น nil โดยอัตโนมัติเมื่อคำขอใช้ HTTPS
    • แม้ค่าภายในจะว่างอยู่ ก็ยังมีประโยชน์สำหรับตรวจสอบว่ามีการบังคับใช้ HTTPS หรือไม่
  • สามารถเข้าถึงชุดพารามิเตอร์ที่เชื่อถือได้ทั้งหมดที่พร็อกซีส่งมาด้วย fcgi.ProcessEnv

เหตุผลที่ยังไม่แพร่หลายและข้อจำกัดในโลกจริง

  • หาก FastCGI ดีกว่า เหตุใดจึงไม่ถูกใช้อย่างแพร่หลาย ผู้เขียนมองว่าส่วนหนึ่งมาจาก ชื่อที่ให้ความรู้สึกเก่า และการที่คนยังตระหนักถึงปัญหาความปลอดภัยของ HTTP reverse proxy ไม่มากพอ
  • Watchfire เคยพูดถึงการโจมตีแบบ desync ตั้งแต่ปี 2005 และเตือนแล้วว่าแก้ได้ไม่ง่าย แต่การโจมตีลักษณะนี้กลับไม่ได้รับความสนใจอย่างจริงจังอยู่นานกว่าสิบปี
  • FastCGI ยังใช้งานจริงได้ในปัจจุบัน และที่ SSLMate ก็ใช้งานใน production มานานกว่าสิบปี
  • อย่างไรก็ตาม เพราะเป็น เทคโนโลยีเก่า จึงมีจุดอ่อนอยู่บ้าง
    • ไม่ได้ถูกอัปเดตเพื่อรองรับ WebSockets
    • ecosystem ของเครื่องมือยังไม่มาก
    • ตัวอย่างเช่น curl รองรับตั้งแต่ FTP, Gopher, SMTP ไปจนถึงอีกหลายโปรโตคอล แต่ไม่สามารถส่งคำขอ FastCGI ได้
  • เมื่อทดสอบ benchmark เซิร์ฟเวอร์ FastCGI ของ Go หลังรีเวิร์สพร็อกซีหลายตัว พบว่าบาง workload มี throughput ต่ำกว่า HTTP/1.1 หรือ HTTP/2
    • ผู้เขียนมองว่านี่ไม่ใช่ข้อจำกัดของตัวโปรโตคอลเอง แต่เป็นผลจาก code path ของ FastCGI ที่ยังไม่ได้รับการปรับแต่งมากเท่ากับ HTTP

ข้อสรุปสุดท้าย

  • หากไม่ต้องใช้ WebSockets และประสิทธิภาพในปัจจุบันเพียงพอ FastCGI ก็ยังเป็นตัวเลือกที่น่าใช้
  • แม้จะเกิดคอขวดขึ้นมา การ เพิ่มฮาร์ดแวร์ ก็ยังน่าจะดีกว่าการยอมรับความซับซ้อนและฝันร้ายด้านความปลอดภัยของการทำ HTTP reverse proxying

2 ความคิดเห็น

 
rtyu1120 4 시간 전

คอมเมนต์เกี่ยวกับ FastCGI ของ Twisted ที่เจอในคอมเมนต์ของ Lobsters น่าประทับใจทีเดียว https://web.archive.org/web/20160723091923/…

 
GN⁺ 4 시간 전
ความเห็นจาก Hacker News
  • เห็นด้วยกับเจตนาของบทความ สำหรับการใช้งานแบบนี้ ผมมองว่า FastCGI ดีกว่า HTTP
    อยากแนะนำโปรโตคอลชื่อ WAS (Web Application Socket) ด้วย เมื่อ 16 ปีก่อนตอนทำงานผมรู้สึกว่าแม้แต่ FastCGI ก็ยังดีไม่พอเลยออกแบบขึ้นมาเอง
    แทนที่จะใช้ main socket framing มันใช้ control socket 1 ตัวกับ pipe 2 ตัวสำหรับ raw request/response body และทั้ง WAS app กับ web server ก็ใช้ splice() กับ pipe ได้
    ไม่ต้องมี framing, ยกเลิก request ได้ และทำให้กู้คืน file descriptor ทั้งสามตัวได้เสมอ
    ใช้มันมาหลายปีทั้งกับแอปภายในและสภาพแวดล้อมเว็บโฮสติ้ง และยังเขียน PHP SAPI เองด้วย เว็บไซต์จำนวนไม่น้อยรันอยู่บน WAS ภายใน
    ทั้งหมดเป็นโอเพนซอร์ส
    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 กับ HTTP ไม่ได้อยู่ในชั้นเดียวกัน
      HTTP มีไว้ส่งข้อมูลระหว่างปลายทั้งสองด้าน เช่น เบราว์เซอร์กับเซิร์ฟเวอร์ ส่วน FastCGI มีไว้ประมวลผลข้อมูลนั้นระหว่างเซิร์ฟเวอร์กับแอปพลิเคชัน
      ผมเพิ่งไล่อ่านบทความนี้คร่าว ๆ แล้วรู้สึกว่าผู้เขียนใช้มันแบบชวนให้สับสนราวกับว่าทั้งสองอย่างแทนกันได้ แต่จริง ๆ แล้วไม่ใช่เลย
      อ้างอิงจากประสบการณ์ ผมเองก็ใช้ fcgi กับบริการเว็บสำหรับลูกค้ามา 10 ปี
  • บทความนี้น่าสนใจเพราะมีหลายอย่างที่ไม่ได้พูดถึง
    ตอนที่ประเด็น FastCGI vs. SCGI vs. HTTP กำลังถกกันหนัก ๆ ผมก่อตั้งสตาร์ตอัป Web2.0 และประกอบฟรอนต์เอนด์สแต็กเอง สุดท้าย HTTP ชนะเพราะความเรียบง่าย
    ถ้าใช้ HTTP ที่เกตเวย์ต้องจัดการอยู่แล้วต่อไปตรง ๆ ก็ไม่ต้องเพิ่มโปรโตคอลอื่นเข้ามาในสแต็ก และทำให้ใส่ reverse proxy ได้หลายชั้น หรือแยก concern แบบข้ามส่วนอย่าง authentication, session, SSL termination, DDoS filtering ออกเป็นเซิร์ฟเวอร์ตามบทบาทได้ง่ายมาก
    ในสภาพแวดล้อมพัฒนา เราต่อเข้า app server ด้วย HTTP โดยตรงได้ ส่วนใน production ก็ใช้ reverse proxy มาจัดการ SSL, authentication และการตรวจจับการใช้งานผิดปกติ โดยยังใช้ app server ตัวเดิมได้เลย
    ตอนนั้น nginx ยังเร็วและเสถียรกว่าโมดูล FastCGI/SCGI ส่วนใหญ่มากด้วย ตอนแรกเราจัดเป็น HTTP -> Lighttpd -> FastCGI -> Django แต่ใช้ nginx ตรง ๆ เร็วกว่ามาก
    การใช้ HTTP ทำงานคล้าย End-to-End Principle ฉบับเว็บ คือเครือข่ายกับโปรโตคอลควรไม่สนใจสิ่งที่ส่งผ่าน และ logic ของแอปควรอยู่ที่ปลายทาง ไม่ใช่อยู่ในโหนดเครือข่ายที่คอยกรองหรือ redirect
    แต่ประเด็นสำคัญที่บทความชี้คือ ในแง่ความปลอดภัย หลายครั้งการยึด หลักสิทธิ์ขั้นต่ำ (PoLP) จะดีกว่า ควร allowlist เฉพาะการสื่อสารที่คาดไว้ เพื่อไม่ให้เผลอช่วยให้การเจาะระบบจากจุดอื่นทำงานได้
    สุดท้ายแล้วสองอย่างนี้มีความตึงกันอยู่ E2E ให้ความยืดหยุ่น แต่ความยืดหยุ่นนั้นก็เพิ่มช่องให้ถูกนำไปใช้ผิด ส่วน PoLP ให้ความปลอดภัย แต่ก็ทำให้ทำได้แค่สิ่งที่ออกแบบไว้ จึงปรับตัวยากเมื่อมีความต้องการใหม่
    [1] https://en.wikipedia.org/wiki/End-to-end_principle
    [2] https://en.wikipedia.org/wiki/Principle_of_least_privilege

    • ผมว่าอุปมานั้นไม่ค่อยตรง โดยเฉพาะในบริบทของ connection caching และ multiplexing
      ถ้าเกตเวย์กลาง multiplex HTTP request หลายตัวลงใน HTTP channel ตัวเดียวที่ต่อไปถึง listening service โดยตรง และไม่ demultiplex ก่อนถึง application socket นั่นถือว่าทำลายตรรกะ end-to-end อย่างพื้นฐานในหลายทาง
      อุปมานั้นพอจะใช้ได้ก็เฉพาะตอนที่ยังคงสมมาตรของการเชื่อมต่อแบบ 1:1 เท่านั้น
      ผมมองว่าช่องโหว่ของ reverse proxy ทั้งหมดเกิดขึ้นโดยตรงจากการละเมิด end-to-end
      ถ้าอุปมานั้นถูก การส่ง SMTP ผ่าน MX หลายตัวก็ควรถือเป็น end-to-end ด้วย แต่ในความจริงมันไม่ใช่ และยังเจอปัญหาคล้าย reverse proxy เยอะ เช่น message boundary desync
      ผมเข้าใจเจตนาที่พยายามเทียบ HTTP request กับข้อความ แต่พอเจอ semantics ของ TCP·HTTP จริง ๆ และรายละเอียดโปรโตคอลสารพัด มันก็พังเร็วมาก
      หลัก end-to-end ไม่ได้ยอมให้จัดการ semantics แบบหยาบ ๆ ได้ มันต้องการวินัยที่เข้มงวดมากเรื่อง state management และขอบเขตของ transport layer ของที่แค่พอคล้าย end-to-end ไม่ใช่ end-to-end
    • สำหรับนักพัฒนาเว็บแอป HTTP semantics มีประโยชน์ แต่ตัว HTTP wire protocol เองแย่มาก
      ตัวอย่างเช่นมันไม่มี multiplexing จนถึง HTTP 2.0 เพราะงั้นการใช้ HTTP ตรง ๆ ระหว่าง reverse proxy กับ backend จึงสิ้นเปลืองมาก
      ยังมีปัญหาด้านความปลอดภัยด้วย เพราะ parser ต่างตัวอาจตีความจุดสิ้นสุดของขอบเขต request ไม่ตรงกันด้วยซ้ำ
      Google เองก็นานมากแล้วที่ใช้โปรโตคอล Stubby ของตัวเองมาห่อ HTTP ระหว่าง front web server กับแอปพลิเคชัน
      มันเร็วกว่า HTTP wire protocol มากและฟีเจอร์ก็เยอะกว่า ปกติอาจเกินความจำเป็นสำหรับบริษัททั่วไป แต่เมื่อสเกลใหญ่ขึ้น ค่าใช้จ่ายในการทำ wire protocol อื่นพร้อม tooling รอบตัวขึ้นมาเองก็สมเหตุสมผลได้
    • การเอา end-to-end principle มาใช้ภายในดาต้าเซ็นเตอร์แทบไม่มีความหมาย และอย่างที่บทความนี้แสดงให้เห็น มันกลับเปิดทางให้เกิดพฤติกรรมที่ไม่ปลอดภัย
    • สิ่งที่ผมไม่ชอบใน nginx คือ เอกสาร จริง ๆ รู้สึกว่าแทบไม่มีประโยชน์
      httpd เองก็เดินไปในทางที่ทำให้การตั้งค่ายากขึ้น ณ จุดหนึ่ง และผมก็เลิกใช้ตอนที่มันเปลี่ยนรูปแบบคอนฟิกแบบหักดิบ
      ผมอาจจะปรับตัวได้ แต่เลือกย้ายไป lighttpd แทน แล้วหลังจากนั้น ruby ก็ช่วยทำคอนฟิกอัตโนมัติ จนในทางเทคนิคผมคงกลับไปใช้ httpd ก็ได้
      ถึงอย่างนั้นก็ไม่อยากกลับไปอยู่ดี ถ้าเป็นนักพัฒนาเว็บเซิร์ฟเวอร์ ควรคิดให้รอบคอบก่อนบังคับให้ผู้ใช้ต้องย้ายไปเข้ารูปแบบใหม่
      ถ้าจะเปลี่ยน format คอนฟิกด้วยเหตุผลง่าย ๆ แบบนั้น อย่างน้อยก็ควรมีตัวเลือกเพิ่มอย่าง yaml config ให้ใช้ด้วย แทนที่จะบังคับ syntax สไตล์ if-clause แบบใหม่ทันที
  • ตอนนี้ที่ WHATWG streams กระจายอยู่ทั่วไปในเบราว์เซอร์แล้ว การทำของคล้าย WebSocket เองบน HTTP request แบบอายุยาวก็ง่ายพอสมควร
    แค่ส่ง byte stream แล้วใส่ header ไว้หน้าข้อความแต่ละอัน ซึ่งหลายกรณีมีแค่ค่าความยาวก็พอ
    มันมีข้อดีด้วย ไม่ต้องมีเส้นทางพิเศษในชั้นเซิร์ฟเวอร์แบบ WebSocket, ใช้ backpressure ได้, ได้ประโยชน์จาก HTTP/2·HTTP/3 ฟรี และมี framing overhead ต่ำกว่า
    แต่เท่าที่ผมรู้ AFAIK ยังไม่รองรับการสตรีม request body ต่อเนื่องไปพร้อมกับรับ response พร้อมกัน ดังนั้นถ้าจะทำ full duplex streaming จริง ๆ ต้องใช้ request สองตัว

  • ผมเพิ่งกลับไปเจอ plain CGI แบบเก่าอีกครั้ง และมันเหมาะมากสำหรับให้ผู้ใช้บนแพลตฟอร์มเราทำหน้าแบบกำหนดเองด้วย vibe code [1]
    ฟังก์ชันที่มีมาให้พื้นฐานคือ task list กับ data viewer แต่ผู้ใช้มักอยากได้การปรับแต่งละเอียดกว่านั้นมาก เช่น Kanban view หรือ custom dashboard ที่มี data filter กับ chart
    ในกล่องนี้มี coding agent อยู่ เราเลยให้ผู้ใช้เขียนสิ่งที่ต้องการเองได้ แทนที่จะต้องมาสร้าง report builder แบบดั้งเดิม
    Go stdlib รองรับดีทั้งฝั่งเซิร์ฟเวอร์และ user space และถ้า coding agent สร้าง page-name/main.go ให้แล้วสื่อสารผ่าน CGI เซิร์ฟเวอร์ก็จะส่งต่อ request ไปที่นั่น
    ขนาดข้อมูลและ pageview ทั้งหมดอยู่ที่ระดับ person scale จึงไม่จำเป็นต้องมี optimization แบบ FastCGI อะไรเป็นพิเศษ
    ในยุคของ agent เทคโนโลยีเก่ากลับมาดูใหม่อีกครั้ง

    1. https://housecat.com
    • ต้องระวังว่า CGI ต่างจาก FastCGI ตรงที่มันส่ง HTTP header ผ่าน environment variable ซึ่งมีจุดอันตรายค่อนข้างใหญ่: https://httpoxy.org/
      implementation ฝั่ง CGI server ของ Go ไม่ได้ตั้งค่า $HTTP_PROXY จึงปลอดภัยในจุดนั้น แต่ถึงอย่างนั้นผมก็ยังไม่ชอบวิธีที่ CGI ใช้ environment variable อยู่ดี
  • ฝั่ง reverse proxy ส่วนใหญ่ทำงานไม่ซับซ้อน จึงใช้ความสามารถที่มีมาในตัวของ Nginx ก็เพียงพอแล้ว
    ถึงอย่างนั้น ถ้าต้องการอะไรที่ซับซ้อนกว่านั้น แนวคิดที่จะใช้ FastCGI ก็อาจไม่ใช่สิ่งที่ผมจะนึกถึง
    เมื่อราว 10 ปีก่อนผมเคยใช้ FastCGI นิดหน่อยเพื่อเอาโค้ด C++ บางส่วนมารันบนเว็บ แต่หลังจากนั้นแทบไม่ได้ใช้อีกเลย

    • ทุกวันนี้ embedded server พบได้บ่อยกว่ามาก
      แค่ใส่ HTTP server เข้าไปในแอปพลิเคชันโดยตรง แล้วจัดการสิ่งที่ต้องทำเองโดยไม่ต้องมีเกตเวย์
  • คอนฟิก PHP/Apache ที่แจกในสาย Red Hat คือ FPM (FastCGI Process Manager)
    ผมไม่แน่ใจว่าในดิสโทร RHEL มีการใช้ FastCGI ที่อื่นอีกหรือเปล่า
    $ rpm -qi php-fpm | grep ^Summary
    Summary : PHP FastCGI Process Manager

    • สิ่งที่คุณน่าจะหาอยู่ไม่ใช่ FPM แต่เป็น mod_proxy_fcgi มากกว่า
      มันอยู่ในแพ็กเกจ httpd-core ของ Fedora ส่วน RHEL ผมไม่แน่ใจ: https://packages.fedoraproject.org/pkgs/httpd/httpd-core/fed...
  • มี uwsgi protocol ด้วย
    มันเองก็มีลักษณะคล้าย RPC สำหรับแทบทุกอย่างเหมือนกัน

  • FCGI ก็เป็นระบบ orchestration ด้วย
    ถ้าโหลดสูงขึ้นมันจะเปิด server task เพิ่ม พอโหลดลดก็ปิดลง และถ้า task ตายก็เปิดสำเนาใหม่
    มันเหมือน Kubernetes สำหรับเครื่องเดียว

    • จากประสบการณ์ผม ฟีเจอร์นั้นไม่ค่อยดีเท่าไร
      ฟังดูดี แต่ที่เจอบ่อยคือโหลดต่ำ ๆ ทำงานได้ดี พอโหลดสูงขึ้นแล้วเริ่มสร้าง worker เพิ่มกลับกินหน่วยความจำจนหมด
      เพราะงั้นการกำหนดจำนวน worker แบบคงที่มักดีกว่า
      แต่ crash recovery ก็มีประโยชน์ถ้าต้องการ
    • ฝั่งเราก็ใช้แบบนั้นเหมือนกันเป๊ะ
  • ขอหยุดชื่นชม ความวิปลาส ของ HTTP header สักครู่
    ถ้าใช้ X-Real-IP เฉพาะตอนที่ไม่มี True-Client-IP ต่อให้ proxy ใส่ X-Real-IP มาอย่างถูกต้อง ผู้โจมตีก็ยังเจาะได้ด้วยการส่ง header True-Client-IP มาด้วย
    มีทั้ง X-Forwarded-For, X-Real-IP, และ custom header เฉพาะของแต่ละ CDN บางอันเป็นรายการคั่นด้วย comma และมักแถม IP ของ LB ฝั่งเราเองมาแบบไร้ประโยชน์ด้วย
    ผมเข้าใจว่าทำไมมันถึงเป็นแบบนั้น แต่มันไม่ได้ช่วยอะไรเลย
    ยิ่งไปกว่านั้น header พวกนี้ทั้งหมดถูก user-agent ที่เป็นอันตรายแทรกเข้ามาได้ด้วย เหมือนว่าไม่มีใครตกลงกันได้เลยว่าเซิร์ฟเวอร์ที่เชื่อถือได้ควรส่งข้อมูลสำคัญผ่าน pipeline อย่างไร
    ความวุ่นวายนี้ยังเข้าคู่กับ ความวิปลาส ของ header User-Agent ได้ดีอีกด้วย
    ฝั่งนั้น Apple ก็ยิ่งพาไปสุดทางด้วยการส่งข้อมูลปลอมแบบเต็มตัว โดยอ้างเรื่องความเป็นส่วนตัว เช่นหมายเลขเวอร์ชัน OS ปลอมอะไรทำนองนั้น

  • ข้อโต้แย้งนี้มีน้ำหนักมาก แต่ FastCGI มีข้อสูญเสียเพราะมันตาม CGI/1.1 ในจุดอย่าง PATH_INFO
    มันบังคับให้ทำ URL decoding เลยไม่สามารถแทน encoded slash อย่าง %2F ได้
    บาง implementation ยังรวม // ใน path ให้เหลือ / ด้วย ซึ่งปัญหานี้ก็พบใน HTTP implementation หลายตัวเหมือนกัน
    ในแง่ expressiveness มันด้อยกว่า HTTP และความต่างนี้จะสำคัญหรือไม่ก็ขึ้นอยู่กับแอปพลิเคชัน
    โดยส่วนตัวผมชอบแนวทางที่จัดการ URL ได้อย่างแม่นยำมากกว่า