Featured image of post Cookies - SameSite Attribute

Cookies - SameSite Attribute

Cookies - SameSite Attribute

Photo by Mohammad Rahmani on Unsplash

ตั้งแต่เวอร์ชัน 84 เป็นต้นมา Chrome ได้กำหนดค่าเริ่มต้นของแอตทริบิวต์ SameSite ของคุกกี้เป็น Lax บริการที่ใช้ Third-party cookies อาจได้รับผลกระทบหากไม่ได้ตั้งค่า SameSite ไว้อย่างเหมาะสม

ภาพรวม

คุกกี้ (Cookies) เป็นกลไกที่ใช้ในบริการเว็บเพื่อเก็บสถานะ มักใช้ในการรักษาการเข้าสู่ระบบ ตะกร้าสินค้า การติดตามโฆษณา ฯลฯ อย่างไรก็ตาม การใช้งานคุกกี้อย่างแพร่หลายก็นำมาซึ่งความกังวลด้านความเป็นส่วนตัวและความปลอดภัย และ SameSite ก็เกิดขึ้นมาเพื่อแก้ไขปัญหาเหล่านี้

 

First-Party and Third-Party

ขึ้นอยู่กับ แหล่งที่มาของคุกกี้ (Set-Cookie) คุกกี้แต่ละตัวจะมีโดเมน (Domain) เฉพาะ เมื่อดูจาก URL ปัจจุบันในเบราว์เซอร์ของผู้ใช้ หากโดเมนของคุกกี้ตรงกับ URL ปัจจุบัน จะถือว่าเป็น First-Party แต่ถ้าไม่ตรง จะถือว่าเป็น Third-Party

Third-party

ตัวอย่างเช่น เมื่อเรียกดูเว็บไซต์ a.com มีการส่งคำขอ (Request) ไปยัง third-party.com และได้รับคุกกี้จาก third-party.com เนื่องจากเบราว์เซอร์จะแนบคุกกี้ที่มีโดเมนเดียวกันไปกับคำขอโดยอัตโนมัติ หากหลังจากนั้นคุณเรียกดูเว็บไซต์อื่น เช่น b.com และมีการส่งคำขอไปยัง third-party.com เซิร์ฟเวอร์ก็จะได้รับคุกกี้ สำหรับทั้งสองเว็บไซต์นี้ คุกกี้ของ third-party.com คือ Third-party

First-party

หากคุณเรียกดูเว็บไซต์ที่ตรงกับโดเมน third-party.com คุกกี้ก็จะถูกส่งไปด้วย ในกรณีนี้ คุกกี้นี้จะเรียกว่า First-party

 

Same-Origin and Same-Site

ตัวอย่างก่อนหน้านี้กล่าวถึงการใช้โดเมนเพื่อกำหนดประเภทของคุกกี้ แต่วิธีที่ดีกว่าคือการดูว่า Site เหมือนกันหรือไม่ สิ่งนี้เกี่ยวข้องกับ Same-origin ที่เห็นกันบ่อยๆ หรือไม่?

Cookies SameSite

Origin

Origin ประกอบด้วย Scheme, Host และ Port วิธีการตัดสินนั้นง่ายมาก: หาก Scheme, Host และ Port ของ URL สองแห่งเหมือนกันทั้งหมด จะถือว่าเป็น Same-origin หากไม่เหมือนกัน จะถือว่าเป็น Cross-origin

Site

การตัดสิน Same-Site เกี่ยวข้องกับ Effective top-level domains (eTLDs) eTLD ทั้งหมดถูกกำหนดไว้ใน Public Suffix List และ Site ประกอบด้วย eTLD บวกกับคำนำหน้า (prefix)

ตัวอย่างเช่น: github.io มีอยู่ใน Public Suffix List เมื่อเพิ่มคำนำหน้า (เช่น a.github.io) จะกลายเป็น Site ดังนั้น a.github.io และ b.github.io จึงเป็น Site สองแห่งที่แตกต่างกัน (Cross-site)

example.com ไม่มีอยู่ใน Public Suffix List แต่มี .com ดังนั้น example.com จึงเป็น Site และ a.example.com กับ b.example.com เป็น Site เดียวกัน (Same-site)

โปรดทราบว่า Site ไม่รวม Port ดังนั้นแม้ว่า Port จะต่างกัน ก็ยังสามารถเป็น Same-site ได้

 

Why SameSite?

กลไกที่ “ทุกคำขอจะแนบคุกกี้ของโดเมนนั้นไปด้วย” ก่อให้เกิดปัญหาด้านความปลอดภัยและปัญหาอื่นๆ ที่สำคัญที่สุดคือ Cross-site request forgery (CSRF)

CSRF

สมมติว่าผู้ใช้เคยเข้าสู่ระบบ example.com และได้รับคุกกี้ เมื่อผู้ใช้เรียกดูเว็บไซต์อันตราย evil.com JavaScript ในเว็บไซต์นั้นสามารถส่ง POST Request ไปยัง example.com/pay?amount=1000 เบราว์เซอร์จะแนบคุกกี้ของ example.com ไปโดยอัตโนมัติ และผู้ใช้ก็จ่ายเงิน 1000 ดอลลาร์โดยไม่รู้ตัว เซิร์ฟเวอร์ไม่สามารถระบุได้ว่าคำขอนี้มาจากที่ใด

ข้อจำกัด

คุกกี้เองไม่สามารถตั้งค่าให้ส่งเฉพาะในสภาพแวดล้อม First-party ได้ ดังนั้นคำขอก็จะแนบคุกกี้ไปในทุกสภาพแวดล้อม เซิร์ฟเวอร์ไม่สามารถระบุแหล่งที่มาของคำขอได้และทำได้เพียงตอบกลับตามปกติ ในขณะเดียวกัน Client ก็สิ้นเปลืองแบนด์วิดท์ในการส่งคุกกี้ที่ไม่มีประโยชน์

ทางแก้

ด้วยแอตทริบิวต์ SameSite เราสามารถกำหนดเงื่อนไขในการส่งคุกกี้ในสภาพแวดล้อมต่างๆ ได้อย่างอิสระ

SameSite

แอตทริบิวต์ SameSite มีค่าสามค่า การตั้งค่าเป็น Strict หรือ Lax สามารถจำกัดให้ส่งคุกกี้เฉพาะในคำขอแบบ Same-Site เท่านั้น หากเว้นว่างไว้ พฤติกรรมจะขึ้นอยู่กับเบราว์เซอร์ สำหรับ Chrome ค่าเริ่มต้นคือ Lax

Strict

คุกกี้จะถูกส่งเฉพาะในสภาพแวดล้อม First-party เท่านั้น แต่มีปัญหาคือ สมมติว่าผู้ใช้เห็นลิงก์โพสต์ Facebook บน example.com (สมมติว่าเป็น fb.com) แม้ว่าผู้ใช้จะเคยเข้าสู่ระบบ fb.com และมีคุกกี้อยู่ แต่เมื่อคลิกลิงก์ คุกกี้จะไม่ถูกส่งเนื่องจากทั้งสองเว็บไซต์เป็น Cross-site ดังนั้นพวกเขาจะเห็นเพียงหน้าเข้าสู่ระบบเท่านั้น

ดังนั้น Strict จึงเหมาะสำหรับการดำเนินการที่ละเอียดอ่อน เช่น การลบโพสต์ การชำระเงิน ฯลฯ

Lax

เพื่อแก้ปัญหาข้อจำกัดที่เข้มงวดเกินไปของ Strict Lax อนุญาตให้ส่งคุกกี้ได้แม้ในสถานการณ์ Cross-site ในกรณีต่อไปนี้:

  • การพิมพ์ URL ในแถบที่อยู่
  • การคลิกลิงก์ <a href="...">
  • การส่งแบบฟอร์ม <form method="GET">
  • การแสดงผลล่วงหน้า (Prerendering) <link rel="prerender" href="...">

กรณีเหล่านี้มีจุดร่วมสองประการ: ทั้งหมดเป็นคำขอแบบ GET และทั้งหมดกระตุ้นให้เกิด Top-level Navigation ซึ่งช่วยหลีกเลี่ยงปัญหาที่ Strict บังคับให้ต้อง เข้าสู่ระบบใหม่ ทุกครั้ง และยังป้องกันการ ส่งคุกกี้โดยไม่รู้ตัวเมื่อเรียกดูเว็บไซต์อื่น อีกด้วย

Lax + POST

อย่างไรก็ตาม เพื่อหลีกเลี่ยงการทำลายขั้นตอนการเข้าสู่ระบบที่มีอยู่บางส่วน ปัจจุบัน Chrome ได้ผ่อนคลายข้อจำกัดเล็กน้อยสำหรับ SameSite=Lax เพื่อให้เวลานักพัฒนาในการปรับตัว

ภายในสองนาทีหลังจากตั้งค่าคุกกี้ ไม่ว่า Request Method จะเป็นอะไร ตราบใดที่มันกระตุ้นให้เกิดการนำทางของหน้าเพจระดับบนสุด (Top-level page navigation) คุกกี้ก็จะถูกส่งไป ซึ่งหมายความว่าหากเบราว์เซอร์เปลี่ยนหน้า เช่น การส่งแบบฟอร์ม <form method="POST">

สำหรับรายละเอียด โปรดดู กระทู้ เกี่ยวกับ Lax + POST

None

ในการส่ง Third-party cookie คุณต้องตั้งค่า SameSite=None; Secure ใช่แล้ว จากนี้ไป หากคุณต้องการส่ง Third-party cookie ในสภาพแวดล้อมการทดสอบ โปรดเตรียม https://localhost ไว้ด้วย

นอกจากนี้ การส่ง Cross-Origin Request ผ่าน XHR/Fetch จำเป็นต้องตั้งค่า withCredentials: true เพื่อส่งคุกกี้และทำให้ Set-Cookie ใน Response header มีผล เซิร์ฟเวอร์ต้องตั้งค่า Access-Control-Allow-Credentials: true ใน Response header ด้วย เพื่อให้ JavaScript สามารถเข้าถึงเนื้อหา Response ได้

เบราว์เซอร์ที่ไม่รองรับ

ไม่ใช่ทุกเบราว์เซอร์ที่รองรับกฎ SameSite ล่าสุด ดังนั้นจึงสามารถเพิ่มวิธีแก้ปัญหาชั่วคราว (Workaround) บางอย่างบนเซิร์ฟเวอร์เพื่อรองรับเบราว์เซอร์หลายตัว:

ตั้งค่าคุกกี้ทั้งสองแบบ

วิธีนี้สามารถแก้ปัญหาได้สำหรับเกือบทุกเบราว์เซอร์ แต่ข้อเสียคือจะมีคุกกี้สองชุด:

Set-cookie: name=value; SameSite=None; Secure
Set-cookie: name-legacy=value; Secure

โค้ดฝั่งเซิร์ฟเวอร์:

if (req.cookies['name']) {
  // ใช้ตัวใหม่ถ้ามี
  cookieVal = req.cookies['name'];
} else if (req.cookies['name-legacy']) {
  // มิฉะนั้นให้ใช้ตัวเก่า
  cookieVal = req.cookies['name-legacy'];
}

User Agent

กำหนดเบราว์เซอร์โดยใช้ User agent ของ Request เพื่อตัดสินใจเนื้อหาของ Set-Cookie วิธีนี้ต้องแก้ไขโค้ดที่ตั้งค่าคุกกี้เท่านั้น โดยไม่ต้องเปลี่ยนส่วน Parsing อย่างไรก็ตาม วิธีการตัดสินนี้มีตัวแปรมากกว่าและทำให้ตั้งค่าคุกกี้ผิดได้ง่ายกว่า

 

ทบทวน

  • คุกกี้ที่ไม่ได้ตั้งค่าแอตทริบิวต์ SameSite จะเป็น SameSite=Lax ตามค่าเริ่มต้น และ ไม่สามารถส่งได้ ในสภาพแวดล้อม Cross-site
  • หากต้องการส่งคุกกี้ใน Cross-site คุณต้องตั้งค่า SameSite=None; Secure
  • คุณสามารถใช้ SameSite sandbox เพื่อทดสอบว่าเบราว์เซอร์ปัจจุบันของคุณสอดคล้องกับกฎ SameSite ล่าสุดหรือไม่

Reference

All rights reserved,未經允許不得隨意轉載
ถูกสร้างด้วย Hugo
ธีม Stack ออกแบบโดย Jimmy