- คุกกี้ HTTP: ข้อมูลชิ้นเล็ก ๆ ที่ใช้เพื่อคงสถานะของเว็บ โดยเบราว์เซอร์จะใส่ไปกับทุก HTTP request หลังจากตั้งค่าแล้วจนกว่าจะหมดอายุ
- ปัญหาที่เกิดขึ้น: โค้ด JavaScript บางชุดทำให้เกิดข้อผิดพลาด เพราะไม่สามารถ parse คุกกี้ในไลบรารีมาตรฐานของ Go ได้
สเปก
- RFC 2109, 2965, 6265: นิยามเริ่มต้นและการอัปเดตของคุกกี้ โดยสเปกเกี่ยวกับค่าคุกกี้ไม่สอดคล้องกันระหว่างเซิร์ฟเวอร์กับเบราว์เซอร์
- ปัญหา:
- สิ่งที่เซิร์ฟเวอร์ต้องส่งกับสิ่งที่เบราว์เซอร์ต้องยอมรับไม่ตรงกัน
- ไม่มีข้อจำกัดเกี่ยวกับค่าคุกกี้ที่เบราว์เซอร์ส่งกลับไปยังเซิร์ฟเวอร์
- ไม่มีแนวทางที่ชัดเจนว่าไลบรารีมาตรฐานควรจัดการ header ของคุกกี้อย่างไร
เว็บเบราว์เซอร์
- Firefox: อนุญาตอักขระบางตัวที่ RFC ไม่แนะนำ
- Chromium: เข้มงวดกว่า Firefox เล็กน้อย แต่ก็ยังอนุญาตอักขระได้หลายตัว
- Safari: เมื่อพบอักขระที่ไม่อนุญาต จะไม่หยุดประมวลผลคุกกี้ แต่จะยอมรับค่าไปจนถึงอักขระตัวนั้น
ไลบรารีมาตรฐาน
- Golang: ทำงานคล้าย RFC แต่อนุญาตช่องว่างและเครื่องหมายจุลภาค
- PHP: เกิดข้อผิดพลาดเมื่อพบ control character บางตัว
- Python: เพิกเฉยคุกกี้ที่ไม่เข้าใจ และหยุดโหลดคุกกี้เพิ่มเติม
- Ruby: ยอมรับทุกอักขระและทำ percent-encoding
- Rust: ยอมรับสตริง UTF-8 ทั้งหมด
ความสำคัญต่อเว็บ
- ปัญหาในโลกจริง: ความกำกวมของสเปกทำให้เว็บไซต์จำนวนมากเกิดข้อผิดพลาดได้ง่าย
- ทางแก้: IETF HTTP Working Group ควรอัปเดตสเปกคุกกี้ และทำให้ชัดเจนว่าเบราว์เซอร์กับภาษาโปรแกรมควรจัดการคุกกี้อย่างไร
ตารางสรุป
- การจัดการคุกกี้ของเบราว์เซอร์และภาษา: แต่ละเบราว์เซอร์และภาษาจัดการคุกกี้ต่างกัน และมีระดับความสอดคล้องกับ RFC ไม่เท่ากัน
1 ความคิดเห็น
ความเห็นจาก Hacker News
คุกกี้มีปัญหาที่คาดไม่ถึงและพฤติกรรมที่ชวนอึดอัดปะปนอยู่ แต่ก็ยังทำงานได้ใน 99.95% ของกรณี ปัญหา cookie shadowing จะเกิดขึ้นเมื่อมีการตั้งคุกกี้ชื่อเดียวกันแต่มีคุณสมบัติคีย์ต่างกัน (เช่น โดเมน, พาธ ฯลฯ) ทำให้ฝั่งแบ็กเอนด์หรือ JS แยกไม่ออกว่าเป็นคุกกี้ตัวไหน
Rust ไม่มีความสามารถในการจัดการคุกกี้อยู่ใน standard library และอ้างอิงพฤติกรรมจาก crate ฝั่ง third-party ชื่อ "cookie" ซึ่งมีตัวเลือก percent encoding เหมือนกับ Ruby
โปรโตคอล HTTP แฝงโปรโตคอลอื่นไว้อีกมากมาย และเบราว์เซอร์กับเว็บเซิร์ฟเวอร์ก็เพิ่มความสามารถหลากหลายเข้าไป ความสามารถเหล่านี้ไม่มีสเปกที่ชัดเจน และไคลเอนต์กับเซิร์ฟเวอร์ก็ไม่สามารถระบุความเข้ากันได้ จึงกลายเป็นสถานการณ์ที่ต้องแบกรับการตัดสินใจผิดพลาดจากอดีตต่อไป
เมื่อราว 10 ปีก่อน ตอนที่มีการทำ session แบบอิงคุกกี้ในโปรเจกต์หนึ่ง เคยเจอปัญหาที่ Safari ใช้งานได้แต่ Chrome ใช้งานไม่ได้ สาเหตุคือความต่างที่เบราว์เซอร์จะไม่ตั้งค่าคุกกี้ถ้ารูปแบบไม่ถูกต้อง
การใช้งานคุกกี้ที่สมเหตุสมผลเพียงอย่างเดียวคือการตั้งค่า opaque token เพื่อให้เซิร์ฟเวอร์รู้จำไคลเอนต์ได้ การที่ไคลเอนต์สามารถประมวลผลค่าที่เซิร์ฟเวอร์จะไม่ส่งออกมาเองนั้นไม่ใช่ปัญหา
คุกกี้เป็นปัญหาที่ซับซ้อน และแทบเปลี่ยนแปลงไม่ได้เพราะปัญหา backward compatibility จึงจำเป็นต้องมีเมกะนิกใหม่ขึ้นมา ตัวอย่างเช่น เมกะนิก NewCookie อาจมาพร้อมมาตรการความปลอดภัยสมัยใหม่และสเปกที่เข้มงวดได้
คุกกี้ควรหายไป และสามารถแทนที่ด้วย authentication header ได้ ถ้ามีวิธีมาตรฐานที่ให้เบราว์เซอร์ยืนยันตัวตนกับเว็บไซต์ได้ก็คงดี น่าเสียดายที่ Basic และ Digest authentication ไม่เพียงพอ
เนื่องจากโค้ดเครือข่ายของ Safari ไม่ได้เป็นโอเพนซอร์ส Foundation port ของ Swift อาจเป็นทางเลือกที่ดีได้ เพราะสามารถตรวจดู control characters และ delete characters ได้จากที่นั่น
การพาร์ส header ของคุกกี้ชวนสับสนมาก "มาตรฐาน" ไม่ได้สะท้อนสิ่งที่มีอยู่จริง และแบ็กเอนด์เซิร์ฟเวอร์ ไลบรารี หรือเฟรมเวิร์กแต่ละตัวก็ยอมรับรูปแบบที่ต่างกัน ถ้าควบคุมทั้งฟรอนต์เอนด์และแบ็กเอนด์ได้ทั้งหมดอาจไม่ใช่ปัญหาใหญ่ แต่เมื่อจำเป็นต้องทำงานร่วมกับอย่างอื่น มันจะซับซ้อนมาก
ตอนทดลองกับภาษา Crystal ก็เคยเจอปัญหาคล้ายกัน พยายามสร้างเว็บสแครปเปอร์แบบง่าย ๆ แต่ HTTP client พื้นฐานกลับพาร์สคุกกี้จำนวนมากที่ถูกตั้งค่าไว้ใน response ไม่ได้และหยุดทำงาน