สูตรคุกกี้ SameSite

Chrome, Firefox, Edge และอีกมากมายจะเปลี่ยนลักษณะการทำงานเริ่มต้นให้สอดคล้องกับข้อเสนอของ IETF ซึ่งก็คือ คุกกี้ที่ดีขึ้นเรื่อยๆ เพื่อให้

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

ฟีเจอร์นี้เป็นลักษณะการทำงานเริ่มต้นตั้งแต่ Chrome 84 เวอร์ชันเสถียรเป็นต้นไป คุณควรอัปเดตแอตทริบิวต์สำหรับคุกกี้ของบุคคลที่สามหากยังไม่ได้อัปเดต เพื่อจะได้ไม่ถูกบล็อกในอนาคต

การสนับสนุนในเบราว์เซอร์ต่างๆ

ดูส่วนความเข้ากันได้ของเบราว์เซอร์ในหน้า Set-Cookie ของ MDN

กรณีการใช้งานสำหรับคุกกี้ข้ามเว็บไซต์หรือคุกกี้ของบุคคลที่สาม

มี Use Case และรูปแบบที่พบบ่อยจำนวนมากที่ต้องส่งคุกกี้ในบริบทของบุคคลที่สาม หากคุณระบุหรือขึ้นอยู่กับกรณีการใช้งานอย่างใดอย่างหนึ่งเหล่านี้ โปรดตรวจสอบว่าคุณหรือผู้ให้บริการอัปเดตคุกกี้ของตนเพื่อให้แน่ใจว่าบริการจะทำงานได้อย่างถูกต้องต่อไป

เนื้อหาภายใน <iframe>

เนื้อหาจากเว็บไซต์อื่นที่แสดงใน <iframe> อยู่ในบริบทของบุคคลที่สาม กรณีการใช้งานมาตรฐานมีดังนี้

  • เนื้อหาที่ฝังซึ่งแชร์จากเว็บไซต์อื่นๆ เช่น วิดีโอ แผนที่ ตัวอย่างโค้ด และโพสต์บนโซเชียลเน็ตเวิร์ก
  • วิดเจ็ตจากบริการภายนอก เช่น ฟังก์ชันการชำระเงิน ปฏิทิน การจอง และการจอง
  • วิดเจ็ต เช่น ปุ่มโซเชียลหรือบริการป้องกันการประพฤติมิชอบที่ไม่ชัดเจนนัก <iframes>

โดยอาจใช้คุกกี้เพื่อรักษาสถานะเซสชัน จัดเก็บค่ากำหนดทั่วไป เปิดใช้สถิติ หรือปรับเปลี่ยนเนื้อหาให้เหมาะกับผู้ใช้ที่มีบัญชีอยู่แล้ว

แผนภาพหน้าต่างเบราว์เซอร์ซึ่ง URL ของเนื้อหาที่ฝังไม่ตรงกับ URL ของหน้าเว็บ
หากเนื้อหาที่ฝังไม่ได้มาจากเว็บไซต์เดียวกับบริบทการท่องเว็บระดับบนสุด แสดงว่าเป็นเนื้อหาของบุคคลที่สาม

นอกจากนี้ เนื่องจากเว็บเขียนได้เป็นปกติ จึงใช้ <iframes> เพื่อฝังเนื้อหาที่ดูในบริบทระดับบนสุดหรือบริบทของบุคคลที่หนึ่งด้วย คุกกี้ที่เว็บไซต์นั้นใช้จะถือว่าเป็นคุกกี้ของบุคคลที่สามเมื่อเว็บไซต์แสดงในเฟรม หากคุณตั้งใจสร้างเว็บไซต์ที่ผู้อื่นตั้งใจให้ฝังได้ง่าย ในขณะเดียวกันก็ต้องใช้คุกกี้ในการทำงานด้วย คุณต้องตรวจสอบด้วยว่าเว็บไซต์เหล่านั้นมีการทำเครื่องหมายไว้สำหรับการใช้งานข้ามเว็บไซต์ หรือคุณจะเลือกให้ไม่มีทางเลือกอื่นก็ได้

คำขอ "ไม่ปลอดภัย" ในเว็บไซต์ต่างๆ

แม้ว่าคำว่า "ไม่ปลอดภัย" อาจฟังดูน่ากังวลเล็กน้อยในที่นี้ แต่เป็นคำขอใดก็ตามที่อาจมีเจตนาให้เปลี่ยนสถานะ บนเว็บที่เป็นคำขอ POST เป็นหลัก คุกกี้ที่มีเครื่องหมาย SameSite=Lax จะส่งไปในการนำทางระดับบนสุดที่ปลอดภัย เช่น การคลิกลิงก์เพื่อไปยังเว็บไซต์อื่น อย่างไรก็ตาม ตัวอย่างเช่น การส่ง <form> ผ่าน POST ไปยังเว็บไซต์อื่นจะไม่มีคุกกี้

แผนภาพแสดงคำขอที่ย้ายจากหน้าหนึ่งไปอีกหน้าหนึ่ง
หากคำขอขาเข้าใช้วิธีการ "ปลอดภัย" ระบบจะส่งคุกกี้

รูปแบบนี้ใช้สำหรับเว็บไซต์ที่อาจเปลี่ยนเส้นทางผู้ใช้ออกไปยังบริการระยะไกลเพื่อดำเนินการก่อนส่งคืน เช่น การเปลี่ยนเส้นทางไปยังผู้ให้บริการข้อมูลประจำตัวบุคคลที่สาม ก่อนที่ผู้ใช้จะออกจากเว็บไซต์ ระบบจะตั้งค่าคุกกี้ที่มีโทเค็นแบบใช้ครั้งเดียวโดยมีความคาดหวังว่าสามารถตรวจสอบโทเค็นนี้ได้ในคำขอที่ส่งคืนเพื่อลดการโจมตีด้วยการปลอมแปลงคำขอข้ามเว็บไซต์ (CSRF) หากคำขอส่งคืนดังกล่าวมาทาง POST ก็จะต้องทำเครื่องหมายคุกกี้เป็น SameSite=None; Secure

ทรัพยากรระยะไกล

ทรัพยากรระยะไกลในหน้าเว็บอาจต้องอาศัยคุกกี้ที่จะส่งไปพร้อมกับคำขอ จากแท็ก <img>, แท็ก <script> และอื่นๆ กรณีการใช้งานทั่วไป ได้แก่ การติดตามพิกเซลและการปรับเปลี่ยนเนื้อหาในแบบของคุณ

ซึ่งรวมถึงคำขอที่เริ่มต้นจาก JavaScript โดย fetch หรือ XMLHttpRequest ด้วย หากมีการเรียกใช้ fetch() ด้วยตัวเลือก credentials: 'include' จะเป็นตัวบ่งชี้ที่ดีว่าอาจมีการเรียกใช้คุกกี้ในคำขอเหล่านั้น สำหรับ XMLHttpRequest คุณควรมองหาอินสแตนซ์ของพร็อพเพอร์ตี้ withCredentials ที่ตั้งค่าไว้เป็น true ข้อมูลนี้เป็นตัวบ่งชี้ที่ดีว่าคำขอเหล่านั้นอาจมีคุกกี้อยู่เป็นอย่างดี คุกกี้เหล่านั้นต้องมีการทำเครื่องหมายอย่างเหมาะสมเพื่อให้รวมอยู่ในคำขอแบบข้ามเว็บไซต์

เนื้อหาภายใน WebView

WebView ในแอปเฉพาะแพลตฟอร์มขับเคลื่อนโดยเบราว์เซอร์และคุณจะต้องทดสอบว่าเป็นไปตามข้อจำกัดหรือปัญหาเดียวกันหรือไม่ ใน Android หาก WebView ขับเคลื่อนโดย Chrome ค่าเริ่มต้นใหม่จะไม่ใช้กับ Chrome 84 โดยทันที อย่างไรก็ตาม จุดประสงค์ในการใช้โซลูชันเหล่านี้ในอนาคต คุณจึงควรทดสอบและเตรียมพร้อมสำหรับกรณีนี้ นอกจากนี้ Android ยังอนุญาตให้แอปเฉพาะแพลตฟอร์มตั้งค่าคุกกี้ผ่าน CookieManager API โดยตรงอีกด้วย เช่นเดียวกับคุกกี้ที่ตั้งค่าผ่านส่วนหัวหรือ JavaScript ให้พิจารณาใส่ SameSite=None; Secure หากมีไว้เพื่อการใช้งานแบบข้ามเว็บไซต์

วิธีติดตั้งใช้งาน SameSite วันนี้เลย

สำหรับคุกกี้ที่จำเป็นเฉพาะในบริบทของบุคคลที่หนึ่งเท่านั้น คุณควรทำเครื่องหมายเป็น SameSite=Lax หรือ SameSite=Strict ตามความจำเป็น คุณยังสามารถเลือกไม่ทำอะไรเลยและอนุญาตให้เบราว์เซอร์บังคับใช้ค่าเริ่มต้นนั้น แต่ยังรวมถึงความเสี่ยงที่จะเกิดลักษณะการทำงานที่ไม่สอดคล้องกันในเบราว์เซอร์ต่างๆ และคำเตือนที่อาจเกิดขึ้นจากคอนโซลสำหรับคุกกี้แต่ละรายการ

Set-Cookie: first_party_var=value; SameSite=Lax

สำหรับคุกกี้ที่จำเป็นในบริบทของบุคคลที่สาม คุณจะต้องตรวจสอบว่าได้ทำเครื่องหมายเป็น SameSite=None; Secure โปรดทราบว่าคุณต้องใช้ทั้ง 2 แอตทริบิวต์ร่วมกัน หากคุณเพียงระบุ None โดยไม่มี Secure คุกกี้จะถูกปฏิเสธ การใช้งานเบราว์เซอร์มีข้อแตกต่างบางอย่างที่ใช้ร่วมกันไม่ได้ คุณจึงอาจต้องใช้กลยุทธ์ในการบรรเทาปัญหาซึ่งอธิบายไว้ในการจัดการไคลเอ็นต์ที่ทำงานร่วมกันไม่ได้ด้านล่าง

Set-Cookie: third_party_var=value; SameSite=None; Secure

การจัดการไคลเอ็นต์ที่ทำงานร่วมกันไม่ได้

เนื่องจากการเปลี่ยนแปลงเพื่อรวม None และลักษณะการทํางานเริ่มต้นการอัปเดตยังค่อนข้างใหม่ เบราว์เซอร์จึงไม่สอดคล้องกันเกี่ยวกับวิธีจัดการการเปลี่ยนแปลงเหล่านี้ คุณไปที่หน้าอัปเดตใน chromium.org เพื่อดูปัญหาที่พบในปัจจุบันได้ แต่ไม่สามารถบอกได้หากเป็นโดยละเอียด แม้ว่าวิธีนี้จะไม่ใช่วิธีที่ดีที่สุด แต่ก็มีวิธีแก้ปัญหาเฉพาะหน้าที่คุณสามารถใช้ได้ในช่วงเปลี่ยนผ่านนี้ แต่กฎทั่วไปคือให้ถือว่าไคลเอ็นต์ที่เข้ากันไม่ได้เป็นกรณีพิเศษ อย่าสร้างข้อยกเว้นสำหรับเบราว์เซอร์ ที่ใช้กฎที่ใหม่กว่า

ตัวเลือกแรกคือการตั้งค่าทั้งคุกกี้แบบใหม่และแบบเดิม

Set-cookie: 3pcookie=value; SameSite=None; Secure
Set-cookie: 3pcookie-legacy=value; Secure

เบราว์เซอร์ที่ใช้ลักษณะการทำงานที่ใหม่กว่าจะตั้งค่าคุกกี้ด้วยค่า SameSite ในขณะที่เบราว์เซอร์อื่นๆ อาจไม่สนใจหรือตั้งค่าไม่ถูกต้อง แต่เบราว์เซอร์เดียวกันดังกล่าวจะตั้งค่าคุกกี้ 3pcookie-legacy ด้วย เมื่อทำการประมวลผลคุกกี้ที่มี เว็บไซต์ควรตรวจหาคุกกี้รูปแบบใหม่ที่มีอยู่ก่อน หากไม่พบคุกกี้ ให้กลับไปใช้คุกกี้เดิม

ตัวอย่างด้านล่างแสดงวิธีทำใน Node.js โดยใช้เฟรมเวิร์กของ Express และมิดเดิลแวร์cookie-parser

const express = require('express');
const cp = require('cookie-parser');
const app = express();
app.use(cp());

app.get('/set', (req, res) => {
  // Set the new style cookie
  res.cookie('3pcookie', 'value', { sameSite: 'none', secure: true });
  // And set the same value in the legacy cookie
  res.cookie('3pcookie-legacy', 'value', { secure: true });
  res.end();
});

app.get('/', (req, res) => {
  let cookieVal = null;

  if (req.cookies['3pcookie']) {
    // check the new style cookie first
    cookieVal = req.cookies['3pcookie'];
  } else if (req.cookies['3pcookie-legacy']) {
    // otherwise fall back to the legacy cookie
    cookieVal = req.cookies['3pcookie-legacy'];
  }

  res.end();
});

app.listen(process.env.PORT);

ข้อเสียก็คือ วิธีนี้ต้องมีการตั้งค่าคุกกี้ซ้ำซ้อนเพื่อให้ครอบคลุมทุกเบราว์เซอร์ และต้องทำการเปลี่ยนแปลงทั้ง ณ เวลาที่ตั้งค่าและอ่านคุกกี้ อย่างไรก็ตาม วิธีนี้ควรครอบคลุมเบราว์เซอร์ทั้งหมดไม่ว่าเบราว์เซอร์จะทำงานเป็นอย่างไร และคุกกี้ของบุคคลที่สามจะยังคงทำงานต่อไปเหมือนเดิม

นอกจากนี้ ณ จุดที่ส่งส่วนหัว Set-Cookie คุณจะเลือกตรวจหาไคลเอ็นต์ผ่านสตริง User Agent ได้ โปรดดูรายการไคลเอ็นต์ที่ใช้ร่วมกันไม่ได้แล้วใช้ไลบรารีที่เหมาะสมสำหรับแพลตฟอร์มของคุณ เช่น ไลบรารี ua-parser-js ใน Node.js ขอแนะนำให้หาไลบรารีเพื่อจัดการการตรวจหา User Agent เนื่องจากคุณคงไม่ต้องการเขียนนิพจน์ทั่วไปเหล่านั้นด้วยตนเอง

ข้อดีของวิธีนี้คือทำการเปลี่ยนแปลงเพียงครั้งเดียวที่จุดตั้งค่าคุกกี้ อย่างไรก็ตาม คำเตือนที่จำเป็นในที่นี้คือ การดักจับข้อมูล User Agent นั้นมีความเปราะบางตามปกติและอาจไม่สามารถตรวจจับผู้ใช้ที่ได้รับผลกระทบได้ทั้งหมด

การรองรับ SameSite=None ในภาษา ไลบรารี และเฟรมเวิร์ก

ภาษาและไลบรารีส่วนใหญ่รองรับแอตทริบิวต์ SameSite สำหรับคุกกี้ แต่การเพิ่ม SameSite=None ยังค่อนข้างใหม่ ซึ่งหมายความว่าคุณอาจต้องแก้ไขการทำงานมาตรฐานบางอย่างในขณะนี้ ซึ่งอธิบายไว้ในที่เก็บตัวอย่าง SameSite รายการใน GitHub

การขอความช่วยเหลือ

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