למה לא לקחתי את דן אברמוב לעבודה

10/12/2021

לא מזמן ראיתי את הוידאו של בן עוואד שעושה ראיון עבודה פיקטיבי לדן אברמוב. אם לא ראיתם עדיין ובא לכם לראות את דן אברמוב נאבק עם CSS שווה להיכנס לכאן: https://www.youtube.com/watch?v=XEt09iK8IXs.

אבל הוויכוח שלי עם דן אברמוב לא היה על CSS אלא על נקודה יותר עדינה של אבטחת מידע. באותו ראיון עוואד שואל את אברמוב מה דעתו על dangerouslySetInnerHTML ואברמוב מגיב באי נחת ניכרת. שניהם מסכימים שמשהו ב API הזה מסורבל וההסבר של אברמוב הוא מעניין. נזכיר רק שב API של ריאקט הם רצו להיות מאוד ברורים כשמישהו פותח דלת לפירצת XSS ולכן במקום לקרוא למאפיין שמעדכן את ה innerHTML של אלמנט במשהו פשוט כמו setInnerHTML הם הוסיפו את המילה dangerously. אבל זה לא הכל. כי בשביל להגדיר את ה innerHTML צריך להעביר למאפיין dangerouslySetInnerHTML אוביקט עם מפתח בשם מיוחד __html, כלומר משהו כזה:

<div dangerouslySetInnerHTML={{ __html: "Hello" }}></div>

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

<div html={{ __dangerousHTML: "Hello" }}></div>

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

// file utils.js
export function alertHtml() {
    return { __html: "<b>alert</b>" };
}

ובקובץ שני הקטע:

import { alertHtml } from './utils.js';

<div dangerouslySetInnerHTML={alertHtml()}></div>

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

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

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

  1. אם אני צריך למחוק קובץ, אני אוודא ששם הקובץ מתאים לתבנית שאני רוצה למחוק ושהתיקיה מתאימה לתיקיה שבה אני מוחק קבצים בדיוק לפני המחיקה, ולא כשאני מקבל את הפרמטר מהמשתמש.

  2. אם אני צריך לפנות לבסיס הנתונים, אני אעשה Quoting לקלט החיצוני ממש לפני שליחת השאילתה לבסיס הנתונים (רצוי באמצעות שימוש ב Bound Variables).

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