אבטחה · לעומק

מחזור החיים הקריפטוגרפי המלא

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

שלב 01 · הצפנה

הסוד שלכם נחתם במכשיר שלכם

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

כשלוחצים על הצפנת הסוד, הדפדפן מגריל מפתח תוכן חדש של 256 ביט מ-WebCrypto CSPRNG ומצפין את המטען ב-AES-256-GCM. GCM היא הצפנה מאומתת: כל מקטע נושא תג של 128 ביט, כך שכל שינוי מאוחר מתגלה ולא מפוענח בשקט.

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

צופןAES-256-GCM, מקטעים של 64 KiB
מפתח תוכן256 ביט, חדש לכל סוד
Nonceקידומת אקראית של 7 בתים + מונה
תג אימות128 ביט לכל מקטע
ריפודPadmé, מינימום 64 KiB

שלב 02 · החזקת המפתח

המפתח מוסתר בקישור, לא בשרתים שלנו

במילים פשוטות: החלק של הקישור שאחרי ה-# מכיל את המפתח – ודפדפנים אף פעם לא שולחים את החלק הזה לשום שרת.

הקישור שלכם נושא זרע (seed) אקראי של 256 ביט ב-fragment של הכתובת – כל מה שאחרי ה-#. לפי מפרט ה-URL, הדפדפן מסיר את ה-fragment, והוא אף פעם לא נכלל בבקשה שנשלחת לשרתים שלנו. מהזרע הזה הדפדפן גוזר, באמצעות HKDF, ערך חיפוש שבעזרתו מאתרים את הסוד, ואת המפתח שפותח את עטיפת מפתח התוכן. השרת רואה רק גיבוב של ערך החיפוש.

הדפדפן של הנמען קורא את הזרע מה-fragment ומבצע את אותה גזירה מקומית. זו המשמעות של ״ללא ידיעה״ (zero-knowledge) כאן: החזקה בטקסט המוצפן לבדו חסרת תועלת בלי הקישור.

מיקום המפתחURL fragment #
נשלח לשרת?אף פעם
זרע32 בתים אקראיים, base64url
גזירהHKDF-SHA-256
השרת שומרSHA-256(lookup) בלבד

שלב 03 · סיסמה אופציונלית

מנעול שני שרק בני אדם מחזיקים

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

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

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

KDFArgon2id
Salt128 ביט אקראי
עטיפהAES-256-GCM, IV של 96 ביט

שלב 04 · העברה ואחסון

רק טקסט מוצפן יוצא מהמכשיר שלכם

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

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

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

בהעברהTLS
באחסוןטקסט מוצפן + מפתחות מגובבים + תפוגה
יומניםאין, לשום סוד

שלב 05 · קבלה והשמדה

הקריאה הראשונה זוכה – ואז הוא נמחק

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

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

לכל סוד יש גם קישור השמדה פרטי. פתיחתו לבדה לא משמידה דבר; האישור מוחק את הטקסט המוצפן מיד – לפני שנמען כלשהו יוכל לקרוא אותו.

חד־פעמיתביעה אטומית, ואז השמדה
חלון הורדה חוזרת15 דקות, רק אותו לקוח
תפוגה15 דקות – 14 ימים
שליטת הבעליםקישור השמדה

מודל איומים

מה shhnote יכול ולא יכול לראות

הגרסה הכנה. כאן בדיוק עובר גבול האמון.

אף פעם לא יוצא מהמכשיר שלכם

מה שאנחנו לא יכולים לראות

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

נשמר בשרתים שלנו

מה שכן נמצא אצלנו

גוש מוצפן שאיננו יכולים לפתוח · קבוצת הגודל המרופדת שלו · גיבובים חד־כיווניים של הערכים שהדפדפן שלכם משתמש בהם כדי למצוא את הסוד או להשמיד אותו · האם הוא חד־פעמי או מוגבל בזמן · זמן תפוגה מעוגל. כל זה חסר תועלת בלי הזרע שבקישור שלכם.

כדאי לדעת

ההסתייגויות הכנות

כל מי שמחזיק בקישור המלא (ובסיסמה, אם הוגדרה) יכול לקרוא סוד חד־פעמי – התייחסו לקישור כאל הסוד עצמו. אחרי שסוד מוצג, הדף דורס את מה שהוא יכול כשעוזבים אותו, אבל JavaScript לא יכול להבטיח זאת; סגרו את הלשונית כשסיימתם. דפדפן שמופעל באוטומציה שמזריקה קלט אמיתי יכול לפתוח סוד חד־פעמי בדיוק כמו אדם – ואז הנמען לא מוצא כלום, וזו התוצאה הכנה. מחיקת סוד מסירה את הרשומה; שאריות מוצפנות עלולות להישאר ביומנים, בשטח הפנוי בדיסק או בגיבויים עד שיידרסו, כטקסט מוצפן ללא מפתח. סודות גדולים שנשמרים מחוץ למסד הנתונים נושאים את הזמן שבו הקובץ נכתב. וכמו בכל אפליקציית רשת, אתם סומכים על הקוד שנשלח לדפדפן שלכם – ובגלל זה shhnote הוא קוד פתוח וניתן לאירוח עצמי.

מפרט קריפטוגרפי

כל הרכיבים, במקום אחד

הצפנה

AES-256-GCM

הצפנה סימטרית מאומתת במקטעים של 64 KiB. מפתח של 256 ביט ותג שלמות לכל מקטע, כך ששינוי או קיטוע מתגלים – ולא רק הופכים את המידע לבלתי קריא.

גזירת מפתחות

HKDF · Argon2id

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

אקראיות

WebCrypto CSPRNG

זרעים, מפתחות, ערכי salt ו-nonces מגיעים מהמחולל המאובטח קריפטוגרפית של הדפדפן. אף פעם לא Math.random(), אף פעם לא צפוי.

החזקת המפתח

URL fragment #

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

בהעברה

TLS

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

באחסון

טקסט מוצפן + גיבובים

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

שאלות טכניות

למי שאבטחה חשובה לו

איך נאכפת ההבטחה החד־פעמית מול מצבי מרוץ (race conditions)?

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

למה ה-fragment של הכתובת ולא פרמטר שאילתה?

פרמטרי שאילתה נשלחים לשרת ונרשמים ביומני גישה, בשרתי פרוקסי ובכלי אנליטיקה. ה-fragment (אחרי #) מוגדר במפרט ה-URL כחלק שנשאר בצד הלקוח – הדפדפן אף פעם לא מכניס אותו לבקשת ה-HTTP. התכונה הזאת היא כל הבסיס של התכנון ללא ידיעה (zero-knowledge). אחרי החשיפה, הדף גם מסיר את ה-fragment משורת הכתובת ומההיסטוריה.

מה מונע מסורקי קישורים לשרוף סודות חד־פעמיים מוקדם מדי?

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

אפשר לאמת את זה או לארח בעצמי?

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

האם משהו נרשם לצורך מניעת שימוש לרעה?

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

טיפים לשיתוף בטוח

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

דיווח על חולשת אבטחה

אם נראה לכם שמצאתם בעיית אבטחה באתר הזה, כתבו לנו לכתובת [email protected] וספרו לנו באופן פרטי לפני שאתם מספרים לאחרים. פרטו מה מצאתם ואיך לשחזר את הבעיה.

אנחנו:

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

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

חולשות בתוכנת shhnote עצמה: ראו את מדיניות האבטחה של הפרויקט.

יצירת סוד קוד המקור ב-GitHub

נסו את זה – זה לוקח עשר שניות

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

יצירת סוד