לדלג לתוכן

תקיפות JWT מוסברות - איך הטוקנים שאמורים לאמת אתכם נשברים

בטח ראיתם JWT באיזה Authorization header, וחשבתם שזה סתם מחרוזת ארוכה ומוזרה שאומרת "אתם מחוברים". זה נכון בגדול, אבל מאחורי המחרוזת הזאת מסתתר עולם שלם של טעויות מימוש שחוזרות על עצמן שוב ושוב, כמעט בכל אפליקציה. אם כבר עברתם את השלב של SQL Injection ו-XSS ואתם מחפשים את הרמה הבאה, JWT הוא בדיוק המקום להסתכל בו.

אז מה זה בעצם JWT

ה-JSON Web Token הוא פורמט טוקן שמורכב משלושה חלקים המופרדים בנקודה - כותרת (header), תוכן (payload), וחתימה (signature). הכותרת אומרת באיזה אלגוריתם חתימה נעשה שימוש, התוכן מכיל את הנתונים בפועל, כמו מי המשתמש ומה ההרשאות שלו, והחתימה אמורה להוכיח שאף אחד לא שינה את התוכן בדרך.

הבעיה היא שהכותרת והתוכן רק מקודדים ב-Base64, לא מוצפנים. כל אחד יכול לפתוח טוקן JWT ולקרוא בדיוק מה כתוב בו. כל האבטחה נשענת על החתימה, ועל זה שהשרת באמת בודק אותה נכון. וכאן מתחילות הבעיות, כי מתברר שהרבה מימושים לא בודקים נכון בכלל.

החולשה שבה הכל התחיל - alg none

הכותרת של JWT מכריזה לשרת איזה אלגוריתם להשתמש כדי לבדוק את החתימה. הבעיה היא ששרתים רבים סומכים על מה שכתוב שם בלי לוודא שזה תואם למה שהם ציפו לקבל. באחד המימושים הראשונים שהתגלו, פשוט אפשר היה לשנות את שדה האלגוריתם לערך none, למחוק את החתימה לגמרי, ולקבל טוקן שהשרת מקבל כתקין.

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

מפתחות חלשים ומפתחות שדלפו

אלגוריתם החתימה הנפוץ ביותר, HS256, משתמש במפתח סודי אחד משותף בין השרתים. אם המפתח הזה חלש, קצר, או מבוסס על מילה נפוצה, אפשר לנסות לפצח אותו בכוח גס מול הטוקן עד שמוצאים את המפתח שמייצר בדיוק את החתימה שמופיעה. ברגע שיש לתוקף את המפתח, הוא יכול לחתום כל טוקן שהוא רוצה, כולל טוקן שאומר שהוא מנהל מערכת.

לא פחות נפוץ הוא המקרה שבו המפתח פשוט דלף - נשאר בקוד שהועלה בטעות למאגר ציבורי, נמצא בקובץ הגדרות חשוף, או הוזלג בהודעת שגיאה. חיפוש שיטתי של מפתחות סודיים במאגרי קוד פתוחים הוא כלי סטנדרטי בארגז הכלים של כל בודק חדירה.

הזרקה דרך kid, jwk ו-jku

כשיש כמה מפתחות אפשריים במערכת, JWT מאפשר לכותרת לכלול שדה בשם kid שאומר לשרת איזה מפתח להשתמש. אם השרת לא מסנן את הערך הזה כראוי, אפשר לתמרן אותו כדי לגרום לשרת להשתמש במפתח שהתוקף שולט בו - למשל על ידי הזרקת נתיב לקובץ ידוע, או אפילו הזרקת SQL אם ה-kid נשלף ממסד נתונים.

דומה לזה, שני שדות מתקדמים יותר, jwk ו-jku, מאפשרים לכותרת עצמה להצהיר על המפתח הציבורי שבו יש להשתמש לאימות, או להצביע על כתובת שממנה יש לשלוף אותו. אם השרת סומך על השדות האלה בלי בקרה, התוקף בעצם אומר לשרת "תאמת אותי מול המפתח שאני בוחר", מה שהופך את כל תהליך האימות לחסר משמעות.

כשאף אחד לא באמת בודק את החתימה

לפעמים הבעיה פשוטה עוד יותר - ספריית JWT בצד השרת פוענחת את הטוקן ומחזירה את התוכן שלו, אבל מישהו בקוד קרא רק לפונקציית הפענוח (decode) ולא לפונקציית האימות (verify). ההבדל קריטי - פענוח רק מפרק את המחרוזת ומחזיר את התוכן, בלי לבדוק שהחתימה תקינה בכלל. במקרה כזה, כל טוקן מומצא עם התוכן שאתם רוצים יתקבל בברכה.

שימוש חוזר ומידע רגיש שיושב בתוך הטוקן

שתי בעיות נוספות שכדאי להכיר. הראשונה היא replay - טוקן שנחשף פעם אחת, למשל בלוג שרת או בתעבורת רשת לא מוצפנת, יכול לשמש שוב ושוב עד שהוא פג תוקף, אם המערכת לא בודקת דברים כמו כתובת מקור או לא תומכת בביטול טוקנים. השנייה היא נתונים רגישים שמישהו החליט לשמור בתוך ה-payload בלי לחשוב פעמיים - כתובות מייל, תפקידים פנימיים, ולפעמים אפילו מזהים פנימיים של מסדי נתונים. מכיוון שכל אחד יכול לפענח את התוכן בלי שום מפתח, כל מה שכתוב שם הוא בגדר מידע גלוי לחלוטין.

למה זה כל כך משמעותי בעולם האמיתי

ה-JWT הפך לתקן הכי נפוץ לאימות ב-API ובאפליקציות מודרניות, בדיוק בגלל שהוא נוח - הוא נטול מצב (stateless), קל להעביר, וקל לאמת. אבל הנוחות הזאת היא בדיוק מה שגורם למפתחים למהר וליישם אותו בלי להבין את כל ההנחות שהוא בנוי עליהן. וכשטוקן האימות עצמו נשבר, לא מדובר בחולשה בפינה אחת של האתר - מדובר בפריצת דלת הכניסה הראשית של כל המערכת.

זה בדיוק אחד הנושאים שאנחנו מפרקים לגורמים, שלב אחר שלב, בקורס פריצת אתרים מתקדם שלנו - כולל תרגול מעשי על מערכות שנבנו במיוחד כדי לתרגל את החולשות האלה בלי לפגוע באף אחד.

ואם אתם באמצע התרגול ונתקעתם, אל תישארו לבד עם זה.

הצטרפו לקהילה בדיסקורד

לסיכום

ה-JWT נראה כמו מחרוזת אקראית ומאובטחת מטבעה, אבל הביטחון הזה שקרי. הוא מאובטח רק כמו המימוש שמאחוריו - וברגע שמבינים איפה נוטים לטעות, אלג'ורתם none, מפתחות חלשים, כותרות שאף אחד לא מסנן, קל הרבה יותר גם לזהות את החולשות האלה כחוקרים, וגם למנוע אותן כמפתחים.