הסבר על עוגיות ועל המשלוח שלהן

עוגיות הן לא וודו ובטח ובטח שהן לא מיותרות. במאמר הזה אני מסביר עליהן ועל כמה תכונות חשובות שלהן.
העוגיה מופיעה ב-headers של הבקשה

לא מזמן קולגה שלי נתקל בבאג מעניין שהוא שבר עליו את הראש. בגדול, חלק גדול מהתהליך היה מתקצר עם הבנה עמוקה יותר של מה זה עוגיה ואיך כל הסיפור עובד. כיוון שהוא אדם מבריק, הבנתי שזה לא בעיה של הבנה אלא בגלל שכל נושא העוגיות הפך ל-low level – אז למה שלא נסביר את זה לעומק? אם אתם מכירים איך עוגיות עובדות, אז דלגו הלאה.

מה זו עוגיה? עוגיה בגדול זה מידע טקסטואלי שנשמר בצד הלקוח. את העוגיה אני יכול ליצור בכמה דרכים שהמוכרת שבהן היא על ידי ג׳אווהסקריפט. הדרך הפשוטה היא באמצעות מניפולציה פשוטה על document.cookie


document.cookie = 'hello'

אם נכנס לכלי המפתחים, נוכל לראות שאכן ה-hello נכנס לסיפור הזה אבל ללא ה-name

כלי המפתחים -> application -> עוגיות
כלי המפתחים -> application -> עוגיות

בגדול, זה מספיק – אבל בדרך כלל מקובל להוסיף שם ותאריך תפוגה לעוגיה. כדי שהדפדפן ידע מתי לאדות את העוגיה ושלנו יהיה יותר קל לקרוא את המידע. למשל:


let a = new Date();
a = new Date(a.getTime() +1000*60*60*24*365);
document.cookie = `mycookie=somevalue; expires=${a.toGMTString()};`;

יצירת עוגיה תקינה עם תאריך תפוגה
יצירת עוגיה תקינה עם תאריך תפוגה

מה שחשוב להבין הוא שכל המידע בעצם הוא ב-strings וזה הופך את כל הסיפור למסובך ולא אינטואיטיבי. שזה מקשה על אחסון מידע סתמי. אבל בדיוק בשביל זה יש לנו את localStorage ואת sessionStorage – כדי שנוכל לאפסן מידע בקלות בצד הלקוח בלי העוגיה. אבל למה כן צריך את העוגיה? הו הו הו – לעוגיה יש כמה תכונות חשובות שמאוד מאוד חשוב להכיר.

הראשונה היא שהעוגיה נשלחת בכל בקשת HTTP לשרת. קל לבדוק את זה. הריצו את הקוד שנמצא למעלה בקונסולה שלכם – הוא ייצור את העוגיה mycookie=somevalue. רפרשו את הדף והסתכלו בבקשה שלכם – תוכלו לראות שהבקשה נשלחת עם העוגיה.

העוגיה מופיעה ב-headers של הבקשה
העוגיה מופיעה ב-headers של הבקשה

שזה מאוד מאוד נוח למטרות אותנטיקציה. העוגיה לא נשלחת כאיזה קסם או ׳וודו׳. היא נשלחת כחלק לגיטימי מה-headers של הבקשה יחד עם הפרמטרים של ה-GET, ה-user agent ושאר החגיגות. לגיטימי!

התכונה השניה היא האפשרות להפוך את העוגיה ל-secure. כלומר היא לא תשלח אם הדומיין הוא לא https. זה נותן הגנה מעטה מאוד אם בכלל. אבל החגיגה העיקרית היא ה-httpOnly. הגדרה של עוגיה כ-httpOnly גורמת לה להשלח אך ורק עם הבקשות ומונעת מג׳אווהסקריפט לגשת אליה.


let a = new Date();
a = new Date(a.getTime() +1000*60*60*24*365);
document.cookie = `mycookie=somevalue; expires=${a.toGMTString()}; Secure; HttpOnly`;

רוצים דוגמה? הנה – כאן אני יוצר שתי עוגיות, אחת עם httpOnly והשניה בלי ואז מנסה לגשת אל העוגיה באמצעות סקריפט – מה אני מקבל? כינים בשקית – רק את העוגיה ללא ה httpOnly:

See the Pen HttpOnly example by Ran Bar-Zik (@barzik) on CodePen.

נחמד, נכון? ולא מסובך במיוחד. זה נועד על מנת למנוע חטיפת session. העוגיות הן חשובות מאוד עבור ה-session – כלומר לשמור חיבור מסוים לאתר אחרי שעשיתי לוגין. חשוב לזכור לא לשים על העוגיות שום מידע קריטי או חשוב. אם אני משתמש בהן למטרת session אז זה קריטי לשים שם token בלבד ולא שם משתמש ובטח ובטח שלא סיסמה. בגלל שהעוגיות נשלחות בכל בקשה, זה די שגר ושכח. אז בגדול ברגע שיצרתי אותן כך, שום סקריפט זדוני או התקפת xss לא יוכלו לגשת אליהן.

רק מה? אם אנחנו משתמשים ב-fetch, חשוב לזכור שהעוגיות לא נשלחות אלא אם כן אנחנו מציינים בפירוש ל-fetch לשלוח אותן!


fetch('https://example.com', {
  credentials: 'include'  
})

וזה מה שידידי פספס. בגלל שהוא התייחס לעוגיות כוודו – הוא ביצע בקשות AJAX עם fetch (או אבסטרקציה שלו) ושכח ש-fetch לא שולח את העוגיות – כלומר הבקשות נשלחות ללא session ואז הוא נפל על 403 – כי השרת לא זיהה אותו. זה עלה לו בכמה שעות דיבוג. זכרו – אם אתם מבצעים קריאת AJAX והיא מחזירה 403 – פיתחו את כלי המפתחים ווודאו שאתם שולחים את הבקשה כמו שצריך. אם אין ב-headers את העוגיות – שימו לב להכניס credentials.

פוסטים נוספים שכדאי לקרוא

גלילה לראש העמוד