วิธีที่ถูกต้องในการลบคุกกี้ที่ฝั่งเซิร์ฟเวอร์


141

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

ดังนั้นฉันจะส่งสิ่งนี้จากเซิร์ฟเวอร์:

Set-Cookie: token=$2a$12$T94df7ArHkpkX7RGYndcq.fKU.oRlkVLOkCBNrMilaSWnTcWtCfJC; path=/;

ซึ่งใช้ได้กับเบราว์เซอร์ทั้งหมด จากนั้นเพื่อลบคุกกี้ฉันส่งคุกกี้ที่คล้ายกันโดยมีexpiresฟิลด์กำหนดไว้สำหรับวันที่ 1 มกราคม 1970

Set-Cookie: token=$2a$12$T94df7ArHkpkX7RGYndcq.fKU.oRlkVLOkCBNrMilaSWnTcWtCfJC; path=/; expires=Thu, Jan 01 1970 00:00:00 UTC; 

และใช้งานได้ดีกับ Firefox แต่ไม่ลบคุกกี้ใน IE หรือ Safari

ดังนั้นวิธีที่ดีที่สุดในการลบคุกกี้คืออะไร (โดยไม่ใช้ JavaScript) วิธี set-the-expires-in-the-past ดูเหมือนว่าใหญ่ และทำไมถึงทำงานใน FF แต่ไม่ได้อยู่ใน IE หรือ Safari?


ดูเพิ่มเติมที่stackoverflow.com/a/20320610/212378
Alexis Wilke

คำตอบ:


209

การส่งค่าคุกกี้ที่มีค่า; expiresต่อท้ายจะไม่ทำลายคุกกี้

ทำให้คุกกี้เป็นโมฆะโดยการตั้งค่าว่างและรวมexpiresฟิลด์ด้วย:

Set-Cookie: token=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT

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


52
ฉันอยากจะแนะนำให้ใช้ข้อความว่างเปล่าเป็นขยะแทน"deleted"เพื่อหลีกเลี่ยงความสับสนในภายหลังด้วยค่าทางกฎหมายที่อาจเกิดขึ้นเท่ากับ "ลบ"
yegor256

8
@raulk ใช่คุณถูกต้อง ตลกที่มันไม่เคยสังเกตมาก่อนหวังว่ามันจะไม่ทำให้เกิดปัญหามากเกินไป yegor256 ค่าว่างควรทำงานในกรณีส่วนใหญ่ ที่เกี่ยวข้อง: บางคนอาจสงสัยว่าทำไมคุกกี้ของพวกเขาถึงไม่ถูกลบออกแม้ว่าจะส่งส่วนหัวนี้แล้วก็ตาม ในกรณีดังกล่าวให้ดูที่คุกกี้จากโดเมนอื่น ตัวอย่างเช่นหลังจากลบfoo=bar; domain=www.example.comแล้วfoo=qux; domain=.example.comจะมีการใช้คุกกี้อื่น
Lekensteyn

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

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

2
-1 เนื่องจากฉันไม่เคยเห็นวิธีการกำหนดค่าเบราว์เซอร์ให้ละเว้นการหมดอายุของคุกกี้และไม่มั่นใจว่ามีเบราว์เซอร์ใดที่มีตัวเลือกดังกล่าว ยิ่งไปกว่านั้นประโยคแรกของคำตอบของคุณหลังจากการแก้ไขตัวหนา @ @JajJarvis เป็นเท็จทันทีสำหรับเบราว์เซอร์ที่สำคัญใด ๆ หรือตัวแทนผู้ใช้ที่เป็นไปตามข้อกำหนด tools.ietf.org/search/rfc6265#section-5.3บอกว่า"ตัวแทนผู้ใช้จะต้องขับคุกกี้ที่หมดอายุทั้งหมดออกจากที่เก็บคุกกี้หากในเวลาใด ๆ คุกกี้ที่หมดอายุจะมีอยู่ในที่เก็บคุกกี้" และเพื่อความรู้ที่ดีที่สุดของฉันนั่นคือสิ่งที่เบราว์เซอร์ทุกอันทำอยู่
Mark Amery

47

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

การใช้Expiresคุณลักษณะในอดีตเพื่อลบคุกกี้นั้นถูกต้องและเป็นวิธีการลบคุกกี้ที่บอกโดยข้อกำหนด ส่วนตัวอย่างของRFC 6255สถานะ:

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

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

  1. หาก [เมื่อได้รับคุกกี้ใหม่] ที่เก็บคุกกี้มีคุกกี้ที่มีชื่อโดเมนและเส้นทางเหมือนกันกับคุกกี้ที่สร้างขึ้นใหม่:

    1. ...
    2. ...
    3. อัปเดตเวลาสร้างของคุกกี้ที่สร้างขึ้นใหม่เพื่อให้ตรงกับเวลาสร้างของคุกกี้เก่า
    4. ลบคุกกี้เก่าออกจากที่เก็บคุกกี้
  2. แทรกคุกกี้ที่สร้างขึ้นใหม่ลงในที่เก็บคุกกี้

คุกกี้คือ "หมดอายุ" หากคุกกี้มีวันหมดอายุในอดีต

ตัวแทนผู้ใช้จะต้องขับไล่คุกกี้ที่หมดอายุทั้งหมดจากที่เก็บคุกกี้หากมีคุกกี้ที่หมดอายุในเวลาใดก็ได้

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

เหตุใด OP ของคำถามนี้จึงสังเกตวิธีการนี้ล้มเหลว แม้ว่าฉันจะไม่ได้ปัดฝุ่นสำเนาของ Internet Explorer เพื่อตรวจสอบพฤติกรรมของมัน แต่ฉันคิดว่ามันเป็นเพราะExpiresค่าของ OP ผิดรูปแบบ! พวกเขาใช้ค่านี้:

expires=Thu, Jan 01 1970 00:00:00 UTC;

อย่างไรก็ตามสิ่งนี้ไม่ถูกต้องทางไวยากรณ์ในสองวิธี

ส่วนไวยากรณ์ของคำสั่งสเปคที่คุ้มค่าของExpiresแอตทริบิวต์จะต้องเป็น

rfc1123 - วันที่กำหนดไว้ใน[RFC2616], ส่วน 3.3.1

ตามลิงค์ที่สองด้านบนเราพบสิ่งนี้เป็นตัวอย่างของรูปแบบ:

Sun, 06 Nov 1994 08:49:37 GMT

และพบว่าคำนิยามไวยากรณ์ ...

  1. ต้องการให้เขียนวันที่ในรูปแบบวันเดือนปีไม่ใช่รูปแบบวันเดือนปีที่ใช้โดยผู้ถามคำถาม

    โดยเฉพาะมันกำหนดrfc1123-dateดังต่อไปนี้:

    rfc1123-date = wkday "," SP date1 SP time SP "GMT"
    

    และกำหนดdate1เช่นนี้

    date1        = 2DIGIT SP month SP 4DIGIT
                 ; day month year (e.g., 02 Jun 1982)
    

และ

  1. ไม่อนุญาตให้UTCใช้เป็นเขตเวลา

    ข้อมูลจำเพาะมีคำสั่งต่อไปนี้เกี่ยวกับสิ่งที่เขตเวลาชดเชยในรูปแบบนี้เป็นที่ยอมรับ:

    การประทับวันที่ / เวลา HTTP ทั้งหมดต้องแสดงใน Greenwich Mean Time (GMT) โดยไม่มีข้อยกเว้น

    มีอะไรมากกว่าถ้าเราขุดลึกลงไปในสเปคเดิมของรูปแบบวันที่และเวลานี้เราพบว่าในสเปคครั้งแรกในhttps://tools.ietf.org/html/rfc822ที่ไวยากรณ์ส่วนรายการ "ยูทาห์" (หมายถึง "เวลามาตรฐานสากล" ) เป็นค่าที่เป็นไปได้ แต่ไม่แสดงรายการ UTC (เวลาสากลเชิงพิกัด) ที่ถูกต้อง เท่าที่ฉันรู้การใช้ "UTC" ในรูปแบบวันที่นี้ไม่เคยถูกต้อง มันไม่ใช่ค่าที่ถูกต้องเมื่อมีการระบุรูปแบบครั้งแรกในปี 1982 และข้อมูลจำเพาะ HTTP ได้นำรูปแบบที่เข้มงวดมากขึ้นมาใช้โดยห้ามการใช้ค่า "โซน" ทั้งหมดนอกเหนือจาก "GMT"

หากผู้ถามคำถามที่นี่ใช้Expiresแอตทริบิวต์เช่นนี้แทน:

expires=Thu, 01 Jan 1970 00:00:00 GMT;

ถ้าอย่างนั้นมันก็น่าจะใช้ได้


15

การตั้งค่า "หมดอายุ" เป็นวันที่ผ่านมาเป็นวิธีมาตรฐานในการลบคุกกี้

ปัญหาของคุณอาจเป็นเพราะรูปแบบวันที่ไม่ธรรมดา IE อาจคาดว่า GMT เท่านั้น


2

ใช้ Max-Age = -1 แทนที่จะเป็น "Expires" มันสั้นกว่าจู้จี้จุกจิกน้อยกว่าเกี่ยวกับไวยากรณ์และ Max-Age มีความสำคัญเหนือกว่าหมดอายุอยู่แล้ว


-1

สำหรับการใช้งาน GlassFish Jersey JAX-RS ฉันได้แก้ไขปัญหานี้โดยวิธีทั่วไปแล้วซึ่งอธิบายพารามิเตอร์ทั่วไปทั้งหมด อย่างน้อยสามพารามิเตอร์ต้องเท่ากับ: name (= "name"), path (= "/") และ domain (= null):

public static NewCookie createDomainCookie(String value, int maxAgeInMinutes) {
    ZonedDateTime time = ZonedDateTime.now().plusMinutes(maxAgeInMinutes);
    Date expiry = time.toInstant().toEpochMilli();
    NewCookie newCookie = new NewCookie("name", value, "/", null, Cookie.DEFAULT_VERSION,null, maxAgeInMinutes*60, expiry, false, false);
    return newCookie;
}

และใช้เป็นวิธีทั่วไปในการตั้งค่าคุกกี้:

NewCookie domainNewCookie = RsCookieHelper.createDomainCookie(token, 60);
Response res = Response.status(Response.Status.OK).cookie(domainNewCookie).build();

และเพื่อลบคุกกี้:

NewCookie domainNewCookie = RsCookieHelper.createDomainCookie("", 0);
Response res = Response.status(Response.Status.OK).cookie(domainNewCookie).build();

สำหรับฉันเมื่อฉันตั้ง maxAge เป็น 0 มันจะแสดงผลคุกกี้ด้วย Max-Age = 0 ซึ่ง Chrome ดูเหมือนว่าจะไม่สนใจ ในRFC 6265 ส่วน 4.1.1จะระบุไวยากรณ์ของ Max-Age เป็น "non-zero-digit" นั่นอาจเป็นเหตุผล แม้ว่าดังที่กล่าวไว้โดย @ JoshC13 ส่วนที่ 5.2.2 พูดถึงการตีความค่าน้อยกว่าหรือเท่ากับศูนย์ ดังนั้นมันจึงขัดแย้งกับที่นั่น ...
Matthijs Wessels

ฉันไม่ทราบรายละเอียด แต่ค่าเหล่านี้เป็นคู่ใช้งานได้จริงใน Chrome และเบราว์เซอร์อื่น: maxAgeInMinutes * 60, หมดอายุ
RoutesMaps.com

1
@MatthijsWessels เยี่ยมมาก! เล็ก ๆ น้อย ๆ ฉันขุดลึกและขัดแย้งในความเป็นจริงโดยเจตนาตามที่ระบุไว้ในคหบดีที่rfc-editor.org/errata/eid3430 จะ "ขยายการทำงานร่วมกัน" ตัวแทนผู้ใช้จะต้องตีความเป็นศูนย์หรือเชิงลบMax-Ageเป็นวันที่แทนได้เร็วและเวลา แต่เซิร์ฟเวอร์ถูกห้ามจากการส่งเช่นMax-Ageค่า ฉันเดาว่าผู้เขียนรู้จักลูกค้าที่มีอยู่ทั้งที่ไม่สามารถจัดการMax-Age=0และเซิร์ฟเวอร์ที่ส่งมาในเวลาที่พวกเขาเขียนข้อมูลจำเพาะและพยายามลดปัญหาจากปลายทั้งสอง
Mark Amery

@ Crimean.us ฉันไม่สามารถทำซ้ำได้อีกเช่นกัน บางทีฉันอาจจะทำผิดพลาด
Matthijs Wessels

@MatthijsWessels ปัญหาการละเว้น Max-Age = 0 ได้รับการแก้ไขในตัวอย่างของฉันโดยการตั้งค่าวันหมดอายุเป็น ZonedDateTime.now (). plusMinutes (maxAgeInMinutes) สำหรับ maxAgeInMinutes = 0 เป็น datetime ปัจจุบัน รหัสนี้ใช้งานได้นานในเว็บแอปพลิเคชั่นจริง
RoutesMaps.com
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.