💡 דוגמה מלאה עובדת זמינה ב‑GitHub:
skip-external-resources-when-signing-dotnet

מבוא

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

טעינת מסמכים בטוחה היא התנהגות של GroupDocs.Signature עבור .NET שמסרבת לבצע את הבקשות הללו. מגרסה 26.9, LoadOptions.SkipExternalResources מוגדר כברירת מחדל ל‑true. מאמר זה משווה בין שלוש מצבי טעינה על אותו מסמך, מראה איך לאפשר מארח אחד מבלי לאפשר את כולם, ומסביר מדוע חתימה על קובץ בלתי מהימן אינה דורשת גישה לרשת כלל.

למה זה חשוב יותר ממה שנשמע

להתקפה יש שם – server‑side request forgery – ושלושה צורות קונקרטיות.

כתובת פנימית שאינה נגישה מהאינטרנט נגישה מהשרת שלך, ולכן מסמך מתוכנן יכול לגרום לשירות שלך למשוך http://169.254.169.254/ או נקודת קצה של מנהל על localhost, ובתלות במה שאתה עושה עם התוצאה, לדלוף אותה. נתיב UNC במסמך יכול לגרום למארח Windows לאמת חיצונית, ולמסור אישורים לשרת שנשלט על ידי תוקף. וקישור למארח שלעולם לא משיב תופס את חוט הטעינה עד לפקיעת הזמן, מה שממלא בצורה זולה ברצף של עובדים.

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

שיטה 1 – ברירת המחדל החדשה

בלי LoadOptions כלל:

using var signature = new Signature(sourcePath);
return SavePagePreview(signature, previewPath);

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

אילו תכונות נחשבות חיצוניות? תמונות מקושרות במקום מוטמעות, שדות INCLUDEPICTURE, תמונות מקושרות במצגות ובגיליונות אלקטרוניים, והתמונות וקבצי ה‑CSS ש‑SVG מתייחס אליהם. תוכן מוטמע נשאר ללא שינוי – הוא כבר בקובץ.

שיטה 2 – רשימת לבן של כתובת אחת

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

var loadOptions = new LoadOptions
{
    WhitelistedResources = new List<string> { trustedAddress }
};

using var signature = new Signature(sourcePath, loadOptions);

הכלל המתאים דורש תשומת לב. הוא מבצע בדיקת תת‑מחרוזת ללא תלות ברישיות על כתובת המשאב, מה שאומר שפרק קצר הוא מסוכן: github תואם ל‑github.attacker.example/payload.png בדיוק כמו המארח שהתכוונת אליו. השתמשו בסכמה, במארח ובנתיב – הדוגמה מוסיפה לרשימת הלבן את raw.githubusercontent.com/groupdocs-signature/.

שיטה 3 – לאפשר הכל

התנהגות לפני גרסה 26.9, עדיין זמינה:

var loadOptions = new LoadOptions { SkipExternalResources = false };

מתאים למסמכים שהיישום שלך יצר. מלכודת אחת שכדאי לציין: המאפיין הישן LoadExternalResources הוא בעל קוטביות הפוכה, ולכן SkipExternalResources = false הוא מה שמחליף את LoadExternalResources = true. העתקת ערך מהמאפיין הישן תהפוך את עמדת האבטחה שלך ללא שגיאה שתודיע לך על כך.

השוואת שלוש השיטות: מתי להשתמש בכל אחת

מצב מתאים ביותר ל‑ יתרונות מרכזיים מגבלות
ברירת מחדל (דילוג) העלאות משתמש, אימייל, קבצים משותפים אין אפשרות לבקשה חיצונית תמונות מקושרות מוצגות כמקומות מציין
רשימת לבן מסמכים שמקשרים למארח שבבעלותך משמר קישורים לגיטימיים התאמת תת‑מחרוזת דורשת פרק ארוך ומדויק
לאפשר הכל קבצים שהמערכות שלך ייצרו תצוגות מקדימות נראות בדיוק כמו לפני משחזרת את חשיפת ה‑SSRF שהברירת מחדל הסירה

האם החתימה דורשת את המשאבים?

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

var options = new QrCodeSignOptions("Approved by GroupDocs.Signature")
{
    EncodeType = QrCodeTypes.QR,
    Left = 400,
    Top = 50,
    Width = 120,
    Height = 120
};

SignResult result = signature.Sign(outputPath, options);

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

מה משתנה כשמשדרגים

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

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

עוזר התצוגה המקדימה, מכיוון שזה לא ברור

שתי שלוש המצבאות למעלה קוראות לעוזר קטן, וזה שווה להציג מכיוון ש‑PreviewOptions אינו מקבל נתיב:

var previewOptions = new PreviewOptions(
    pageData => File.Create(previewPath),
    (pageData, pageStream) => pageStream.Dispose())
{
    PreviewFormat = PreviewOptions.PreviewFormats.PNG
};

signature.GeneratePreview(previewOptions);

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

שיטות עבודה מומלצות

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

מה קורה עם SVG

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

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

סיכום

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

משאבים נוספים