3 คะแนน โดย GN⁺ 2024-11-22 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • คุกกี้ HTTP: ข้อมูลชิ้นเล็ก ๆ ที่ใช้เพื่อคงสถานะของเว็บ โดยเบราว์เซอร์จะใส่ไปกับทุก HTTP request หลังจากตั้งค่าแล้วจนกว่าจะหมดอายุ
  • ปัญหาที่เกิดขึ้น: โค้ด JavaScript บางชุดทำให้เกิดข้อผิดพลาด เพราะไม่สามารถ parse คุกกี้ในไลบรารีมาตรฐานของ Go ได้

สเปก

  • RFC 2109, 2965, 6265: นิยามเริ่มต้นและการอัปเดตของคุกกี้ โดยสเปกเกี่ยวกับค่าคุกกี้ไม่สอดคล้องกันระหว่างเซิร์ฟเวอร์กับเบราว์เซอร์
  • ปัญหา:
    1. สิ่งที่เซิร์ฟเวอร์ต้องส่งกับสิ่งที่เบราว์เซอร์ต้องยอมรับไม่ตรงกัน
    2. ไม่มีข้อจำกัดเกี่ยวกับค่าคุกกี้ที่เบราว์เซอร์ส่งกลับไปยังเซิร์ฟเวอร์
    3. ไม่มีแนวทางที่ชัดเจนว่าไลบรารีมาตรฐานควรจัดการ header ของคุกกี้อย่างไร

เว็บเบราว์เซอร์

  • Firefox: อนุญาตอักขระบางตัวที่ RFC ไม่แนะนำ
  • Chromium: เข้มงวดกว่า Firefox เล็กน้อย แต่ก็ยังอนุญาตอักขระได้หลายตัว
  • Safari: เมื่อพบอักขระที่ไม่อนุญาต จะไม่หยุดประมวลผลคุกกี้ แต่จะยอมรับค่าไปจนถึงอักขระตัวนั้น

ไลบรารีมาตรฐาน

  • Golang: ทำงานคล้าย RFC แต่อนุญาตช่องว่างและเครื่องหมายจุลภาค
  • PHP: เกิดข้อผิดพลาดเมื่อพบ control character บางตัว
  • Python: เพิกเฉยคุกกี้ที่ไม่เข้าใจ และหยุดโหลดคุกกี้เพิ่มเติม
  • Ruby: ยอมรับทุกอักขระและทำ percent-encoding
  • Rust: ยอมรับสตริง UTF-8 ทั้งหมด

ความสำคัญต่อเว็บ

  • ปัญหาในโลกจริง: ความกำกวมของสเปกทำให้เว็บไซต์จำนวนมากเกิดข้อผิดพลาดได้ง่าย
  • ทางแก้: IETF HTTP Working Group ควรอัปเดตสเปกคุกกี้ และทำให้ชัดเจนว่าเบราว์เซอร์กับภาษาโปรแกรมควรจัดการคุกกี้อย่างไร

ตารางสรุป

  • การจัดการคุกกี้ของเบราว์เซอร์และภาษา: แต่ละเบราว์เซอร์และภาษาจัดการคุกกี้ต่างกัน และมีระดับความสอดคล้องกับ RFC ไม่เท่ากัน

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

 
GN⁺ 2024-11-22
ความเห็นจาก 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 ไม่ได้และหยุดทำงาน