אבטחה

    למה מידע רגיש דולף דרך כתובות URL ואיך מונעים את זה

    היסטוריית הדפדפן, יומני שרת, רשתות CDN וכותרות Referer שומרים את ה-URL המלא, גם ב-HTTPS. איפה קישורים דולפים ואיך מתכננים כתובות URL שמותר להן לדלוף.

    W. Miller · עודכן · על LinkPilot

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

    איפה ה-URL דולף

    • היסטוריית הדפדפן. כל URL שהמשתמש מבקר בו נשמר מקומית, ויכול להסתנכרן למכשירים אחרים וגם לשירות הסנכרון של יצרן הדפדפן.
    • יומני הגישה של שרת ה-HTTP. רוב השרתים רושמים כברירת מחדל את נתיב הבקשה המלא ואת מחרוזת השאילתה. לכל מי שיש גישה ליומנים יש גם את ה-URL.
    • פרוקסי הפוך ורשתות CDN. Cloudflare, Fastly, מאזן העומסים שלכם: כולם רושמים את ה-URL, וכל אחד מהם לפי מדיניות שמירה משלו.
    • צינורות לאיסוף יומנים. יומני גישה כמעט אף פעם לא נשארים על מכונה אחת. הם נשלחים לשירותי לוגים וניטור מרכזיים, וכך גדל מספר המערכות שמחזיקות את ה-URL.
    • כותרות Referer. כשהדף מקשר למשאב של צד שלישי, הדפדפן יכול לשלוח את ה-URL של דף המקור בכותרת Referer. סקריפטים של סטטיסטיקות, סרטונים מוטמעים ופיקסלים של פרסומות מקבלים אותו.
    • תוספים לדפדפן. תוספים עם הרשאה לקרוא את היסטוריית הגלישה רואים כל URL, כולל החלק שאחרי הסימן #.
    • סנכרון סימניות. כתובות URL שנשמרו בסימניות מסתנכרנות בין המכשירים של המשתמש ולשרתים של יצרן הדפדפן.
    • בוטים של תצוגה מקדימה. Slack, Teams, iMessage, LinkedIn ותוכנות אימייל טוענים את ה-URL כדי להציג תצוגה מקדימה, בשקט, ברגע שההודעה נשלחת.
    • פרוקסי ארגוניים ושערי אימייל. ארגונים רבים בודקים, משכתבים ורושמים כל URL שחוצה את הגבול שלהם.

    האם HTTPS שומר על פרטיות ה-URL?

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

    TLS מצפין את הנתיב ואת מחרוזת השאילתה בזמן המעבר: מי שמאזין לרשת בין הדפדפן לשרת לא יכול לקרוא את הכתובת /reset?token=.... מה ש-TLS לא מסתיר: את שם המארח של היעד (שגלוי בשאילתת ה-DNS ובשדה ה-SNI של TLS ברוב החיבורים כיום), ואת כל מה שקורה בקצוות. הדפדפן עדיין רושם את ה-URL המלא בהיסטוריה, והשרת עדיין כותב אותו ליומן הגישה שלו. הצפנה בזמן המעבר מגינה על הקו, לא על שני הקצוות, ובקצוות בדיוק כתובות URL דולפות.

    דוגמה מעשית: חייו של URL אחד

    עקבו אחרי URL רגיש אחד, למשל קישור לאיפוס סיסמה, https://example.com/reset?token=8f3a91..., מהשליחה ועד הלחיצה:

    1. ספק האימייל שומר את ההודעה (כולל ה-URL) ללא הגבלת זמן, וסורק האבטחה שלו עשוי לטעון את ה-URL כבר במסירה.
    2. שער האימייל של הנמען משכתב את ה-URL ורושם אותו, ואחר כך טוען אותו שוב ברגע הלחיצה.
    3. DNS מתרגם את example.com לכתובת: שם המארח (לא הנתיב) נמצא עכשיו ביומנים של שרת ה-DNS.
    4. שרת האינטרנט כותב את שורת הבקשה המלאה ליומן הגישה שלו. רשומה בפורמט "combined", ברירת המחדל של nginx/Apache, נראית כך:
    203.0.113.7 - - [14/Jul/2026:09:12:45 +0000]
      "GET /reset?token=8f3a91... HTTP/1.1" 200 5123 "-" "Mozilla/5.0 ..."
    
    1. ה-CDN שלפניו רושם את אותו URL, עם מדיניות שמירה משלו.
    2. סקריפט הסטטיסטיקות בדף רושם את ה-URL של הדף, כולל מחרוזת השאילתה, במערכת של צד שלישי, אלא אם הוגדר במפורש להסיר אותה.
    3. הדפדפן שומר את ה-URL בהיסטוריה ומסנכרן אותו.

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

    כותרת Referer בפועל

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

    דפדפנים מודרניים משתמשים כברירת מחדל ב-strict-origin-when-cross-origin (MDN: Referrer-Policy), שמקצר את ה-referrer בבקשות למקור אחר כך שנשאר רק המקור (https://example.com/ במקום הנתיב המלא). זו התקדמות אמיתית, אבל לא כדאי לסמוך עליה: בקשות לאותו מקור עדיין נושאות את ה-URL המלא, מדיניות ברמת הדף יכולה להחליש את ברירת המחדל, ותוכנות ישנות ו-webview מוטמעים מתנהגים כל אחד אחרת.

    שתי דרכים מעשיות לצמצם את הבעיה:

    <!-- Strip the referer for outbound requests on the page -->
    <meta name="referrer" content="no-referrer" />
    
    Referrer-Policy: no-referrer
    

    הגרסה של כותרת ה-HTTP חלה על כל האתר; תגית ה-meta חלה על הדף הספציפי. השתמשו בשתיהן, כדי שתהיה שכבת הגנה נוספת. ועוד יותר טוב: הגישו דפים רגישים בלי שום משאבים של צד שלישי, כך שלא יהיה למי לשלוח referrer.

    האם טוקן בטוח יותר בנתיב, במחרוזת השאילתה או בחלק שאחרי הסימן #?

    רק החלק שאחרי הסימן # לא נשלח בבקשה, ולכן רק הוא לא מגיע ליומני השרת; הנתיב ומחרוזת השאילתה שקולים. המקום שבו שמים את הטוקן ב-URL קובע מי רואה אותו:

    מיקוםנשלח לשרת?ביומני השרת?ב-Referer?בהיסטוריית הדפדפן?
    נתיב (/s/abc123)כןכןתלוי במדיניותכן
    שאילתה (?token=abc123)כןכןתלוי במדיניותכן
    החלק שאחרי # (#abc123)לאלאלאכן

    החלק שאחרי הסימן # באמת שונה: לפי RFC 3986, סעיף 3.5 הוא מעובד בצד הלקוח ואף פעם לא נכלל בבקשת ה-HTTP. לכן כלי הדבקה עם הצפנה מקצה לקצה שמים בו את מפתח הפענוח: השרת לא מקבל אותו בכלל. ובכל זאת הוא לא בלתי נראה. היסטוריית הדפדפן, תוספים וכל JavaScript שרץ בדף יכולים לקרוא אותו. ההבדל בין נתיב לשאילתה, לעומת זאת, לא משנה דבר מבחינת פרטיות: שניהם מגיעים לאותם יומנים.

    הבעיה של טוקנים לאיפוס סיסמה

    טוקנים רגישים בתוך URL הם סוג מוכר של פגיעות. CWE-598 של MITRE ("Use of GET Request Method With Sensitive Query Strings") מתאר בדיוק את זה: מידע רגיש במחרוזות שאילתה מגיע ליומנים, להיסטוריות ול-referrer, בלי קשר להצפנה בזמן המעבר. לכן תהליכי איפוס סיסמה שבנויים היטב אף פעם לא סומכים רק על כך שה-URL נשאר סודי. הם הופכים את הטוקן לחד-פעמי, לקצר חיים ולקשור לפעולה שבאה אחריו (הגדרת סיסמה חדשה), כך שטוקן שנמצא ביומן שלושה שבועות אחר כך לא שווה כלום.

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

    תכנון כתובות URL שמותר להן לדלוף

    הניחו שכל URL שאתם יוצרים יופיע בסוף במקום לא צפוי, ותכננו כך שזה לא ישנה:

    • שימוש חד-פעמי. מחקו את הסוד אחרי הקריאה המוצלחת הראשונה, כך ש-URL שדלף כבר לא יעבוד כשהוא יופיע בהיסטוריה של מישהו.
    • תפוגה קצרה. הגבילו את חלון הדליפה. URL שעבד חמש דקות שווה הרבה פחות מ-URL שעבד שבוע.
    • ביטוי סיסמה שנמסר בערוץ נפרד. דרשו סוד שמעולם לא היה ב-URL. כך ה-URL לבדו, הדבר היחיד שיש ביומנים, לא מספיק.
    • הפרדה בין טעינה להצגה. החזירו מטא-נתונים בבקשת GET ושחררו את התוכן רק בבקשת POST מפורשת, כדי שבוטים של תצוגה מקדימה וסורקים לא יוכלו לצרוך את הסוד.
    • בלי משאבים של צד שלישי בדפים רגישים, ובנוסף Referrer-Policy: no-referrer ו-<meta name="robots" content="noindex">.
    • הסתירו נתיבים סודיים ביומנים שלכם. ב-nginx, מפו את ה-URI של הבקשה לפני הרישום (ראו את התיעוד של ngx_http_log_module לגבי log_format):
    map $request_uri $loggable_uri {
        ~^/s/   "/s/[redacted]";
        default $request_uri;
    }
    log_format redacted '$remote_addr - $remote_user [$time_local] '
                        '"$request_method $loggable_uri" $status';
    access_log /var/log/nginx/access.log redacted;
    

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

    מודל האיומים הזה הוא הסיבה שהקישורים הסודיים של LinkPilot עובדים כמו שהם עובדים: ה-URL נחשב כמשהו שידלוף בהכרח, ולכן ה-URL לבדו אף פעם לא מספיק. בקשת GET מחזירה רק מטא-נתונים; כדי לקבל את התוכן צריך הצגה מפורשת בבקשת POST, ואפשר להגן עליה גם בביטוי סיסמה שעובר hash של SHA-256 בצד הלקוח. סודות שנמחקו, שפג תוקפם או שבוטלו מחזירים HTTP 410, והתוכן שלהם נדרס באופן בלתי הפיך. תגובות ההצגה נושאות Cache-Control: no-store, ואירועי צפייה רושמים hash של כתובת ה-IP עם salt שמתחלף כל יום, ולא את הכתובת עצמה. ומגבלה אחת שאנחנו מציינים בגלוי בדף ארכיטקטורת האבטחה: מפתח הפענוח נמצא בחלק של הקישור שאחרי הסימן #, ולכן צריך להתייחס לקישור כולו כאל הסוד. זו בדיוק הסכנה שהמאמר הזה עוסק בה. תפוגה קצרה, מחיקה לאחר קריאה וביטוי סיסמה שנמסר בערוץ נפרד הם מה שמגביל את הנזק כשקישור דולף.

    עדכון, 29 בספטמבר 2026: במקור נכתב בפסקה הזו ש-LinkPilot לא מציע הצפנה מקצה לקצה. עכשיו הוא מצפין בדפדפן תוכן טקסט, כולל קבצים מצורפים.

    נסו בחינם, בלי הרשמה

    הודעה שמשמידה את עצמה

    שלחו פתק שנעלם אחרי קריאה אחת.

    פתיחת הכלי

    מגבלות

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

    מקורות

    כלים למאמר הזה

    שאלות נפוצות

    LinkPilot שולח קישור סודי שנמחק אחרי שפותחים אותו.

    מוצפן בדפדפן שלכם, מוצג פעם אחת ואז מושמד. ללא חשבון.

    יצירת קישור סודי

    לקריאה נוספת

    אבטחה

    שיתוף מאובטח בקישורים למפתחים

    מדריך מעשי לצוותי פיתוח: מתי להשתמש בקישורים חד-פעמיים, איך ליצור אותם מסקריפט ואיך לשלב אותם בקליטת עובדים וב-CI.

    אבטחה

    קישורים סודיים מול מנהלי סיסמאות

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

    אבטחה

    סורקי קישורים וסודות חד-פעמיים

    קישור חד-פעמי שנמחק בבקשת GET רגילה מת ברגע ש-Slack או שער אבטחה של אימייל יוצרים לו תצוגה מקדימה. החזירו מטא-דאטה ב-GET ומחקו רק בהצגה מפורשת.