התייעצות בענייני קוד מאובטח

user32

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

נגיד שיש לנו פיצ'ר מסויים שמציג טבלה ממויינת. המיון יכול להתבצע לפי לוגיקות שונות. כל לקוח והשגעונות שלו. הרעיון שלנו היה שהלקוח יוכל לכתוב במסך פונקציית JS שהיא למעשה המימוש של sort(a,b)
מבחינתו זה קצת כמו לכתוב נוסחה באקסל ויש אינגרטורים שיודעים לעשות את העבודה. אציין שהקוד ירוץ בשרת.
יש לנו כמה use cases כאלה, חלקם מורכבים יותר ממיון ודורשים העברה והחזרה של אובייקטי JS.

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

BravoMan

Active member
אל תיעלב, אבל משהו בתיאור ה-"פיצ'ר" נשמע לי בעייתי.
אני מקבל פה רושם של "ללכת עם, להרגיש בלי".

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

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

user32

Well-known member
מנהל
אל תיעלב, אבל משהו בתיאור ה-"פיצ'ר" נשמע לי בעייתי.
אני מקבל פה רושם של "ללכת עם, להרגיש בלי".

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

אם מותר לי לשאול: המערכת שבה יהיה קיים הפיצ'ר, זה משהו ששייך ללקוח ורץ אצלו, או שהיא מסופקת ללקוח כ-SaaS?
כלומר, איך מגיעים למצב שנותנים למישהו לערוך קוד, אבזמן שלא בוטחים בו?
קודם כל, לא נעלב :)

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

קלייטון.ש

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

user32

Well-known member
מנהל
לא הבנתי. .אם הלקוח רוצה להשתמש בindexOf, substring, להריץ regex וכו', איך זה יעבוד אם הוא צריך להשתמש רק בthis?
עיבוד פשוט זה בטוח לא. מי שחושב שזה "פשוט" זה ההוא שיזריקו לו דברים שהוא לא חשב עליהם.
 

קלייטון.ש

Well-known member
הפונקציות שציינת הן איברים של משתנה מסוג string.
נניח שהלקוח מגדיר איבר name. כתב בהתחלה let name;
עכשיו כל קריאה שלו לפונקציות שציינת והדומות להן הן מתחת name.
name.substring וכו.
אם אתה מחייב אותו לכתוב this.name.substring (והנחית אותו להגדיר את name כאיבר האובייקט) או שאתה עובר ומוסיף this לכל הביטויים שמצאת תחת let, אז כל פניה שלו לעשות substring יכולה להיות רק תחת האיברים שנמצאים באובייקט הפרטי שלו. או שהוא הגדיר או שהעיבוד שלך הוסיף.
כמובן צריך לאסור על eval ולבדוק שהוא לא נמצא בקוד שנכתב.
יש סכנה של דברים שלא חשבנו עליהם אבל נראה לי שאם תחייב this בצורה אגרסיבית אז במקרה של באג בעיבוד שלך יותר סביר שהקוד לא יעבוד, או שתדחה אותו שלא בצדק, מאשר שהוא יצליח לפנות למשאבים גלובליים.
 

user32

Well-known member
מנהל
אם אני עושה הכל עם this, אז מה אם למשל יירצו להשתמש בפונקציות כמו Math.round שזה די סביר שצורך כזה יתעורר.
בכל מקרה, חשבתי יותר על משהו כמו להשתמש בfork שייצור worker נפרד (מנוע V8) ולדאוג שירוץ עם הרשאות מבודדות. כל זה בנוסף לדברים שהצעתם, כן להדביק כמה שיותר על this, לזהות שימושים לא מקובלים כמו eval, לא לאפשר ייבוא חבילות חיצוניות וכו'.
 

קלייטון.ש

Well-known member
אם אני עושה הכל עם this, אז מה אם למשל יירצו להשתמש בפונקציות כמו Math.round שזה די סביר שצורך כזה יתעורר.
בכל מקרה, חשבתי יותר על משהו כמו להשתמש בfork שייצור worker נפרד (מנוע V8) ולדאוג שירוץ עם הרשאות מבודדות. כל זה בנוסף לדברים שהצעתם, כן להדביק כמה שיותר על this, לזהות שימושים לא מקובלים כמו eval, לא לאפשר ייבוא חבילות חיצוניות וכו'.
יש כמובן סדרה של מילות מפתח שאתה מרשה בלי this. כל ה-if else while וכולי. אז גם Math.
 

user32

Well-known member
מנהל
יש כמובן סדרה של מילות מפתח שאתה מרשה בלי this. כל ה-if else while וכולי. אז גם Math.
מתכון לבאגים, פרצות, אינספור תיקונים ("אני רוצה להשתמש בXYZ וזה לא נותן לי").
סתם דוגמה: מישהו יכול לכתוב:

while (true)
console.log....

זה יספיק לתקוע את השרת או לפחות את הפרוסס הנוכחי של הnode וזה מספיק חמור מבחינתי. אז להגביל גם while? אפשר לעשות אותו דבר עם for כמדומני. לדעתי לא חשבת מספיק עד הסוף על העניין.

אני אחפש פתרונות יותר גנריים. משהו בסגנון הזה:
 
נערך לאחרונה ב:

קלייטון.ש

Well-known member
מתכון לבאגים, פרצות, אינספור תיקונים ("אני רוצה להשתמש בXYZ וזה לא נותן לי").
סתם דוגמה: מישהו יכול לכתוב:

while (true)
console.log....

זה יספיק לתקוע את השרת או לפחות את הפרוסס הנוכחי של הnode וזה מספיק חמור מבחינתי. אז להגביל גם while? אפשר לעשות אותו דבר עם for כמדומני. לדעתי לא חשבת מספיק עד הסוף על העניין.

אני אחפש פתרונות יותר גנריים. משהו בסגנון הזה:
הקוד המסויים הזה ייכשל כי אין לו גישה ל-console כי אתה או תוסיף לפניו this או תדחה את הקוד כי חסר ה-this. אתה לא מרשה גישה למשאבים גלובליים, זה העיקרון.
אבל זה נכון שהקוד שהלקוח מספק לך יכול לדרוש זמן עיבוד ארוך, לא בזדון או טעות אלא כי הוא עושה הרבה עבודה. הקוד של הלקוח תקין גם מבחינתך, רק לוקח לו עשר דקות לבצע כל השוואה.
אפשר לפתור את זה או באמת בהעברת העיבוד ל worker ומהתהליך הראשי לעצור אותו אחרי זמן מוגדר.
עוד אפשרות היא לשתול קריאות לבדיקת זמן עיבוד בהתחלה של כל לולאה. מוצאים את ה-while ו-for ומיד אחרי ה } הראשון דוחפים קריאה לפונקציה שבודקת כמה זמן הלולאה עובדת וחותכת את כל העיבוד אם הוא יותר ממה שמרשים.
(בטח שלא חשבתי עד הסוף ואני מתאר לעצמי שהפרויקט יוליד עוד בעיות, אבל ככה זה בפיתוח תוכנה)

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

קלייטון.ש

Well-known member
מתכון לבאגים, פרצות, אינספור תיקונים ("אני רוצה להשתמש בXYZ וזה לא נותן לי").
סתם דוגמה: מישהו יכול לכתוב:

while (true)
console.log....

זה יספיק לתקוע את השרת או לפחות את הפרוסס הנוכחי של הnode וזה מספיק חמור מבחינתי. אז להגביל גם while? אפשר לעשות אותו דבר עם for כמדומני. לדעתי לא חשבת מספיק עד הסוף על העניין.

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

rj222

New member
פחות מכיר שרתים.
הטכניקה המקובלת בקליינט היא לעשות replace לביטויים בעייתיים שיכולים לשמש ל-code injection.
גוגל קצר לצד שרת:
 

user32

Well-known member
מנהל
הרעיון של כל הפיצ'ר הוא לאפשר code injection. לא למנוע אותו. אני רוצה לאפשר הזרקה של קוד ולמנוע הרצה של קוד מזיק.
 

Nuke1985

Active member
אולי אפשר ללכת על הפיתרון ה"פרימיטיבי" של לאפשר לבן אדם להוציא דוחות ואז הוא מעבד אותן איך שהוא רוצה באיזה טכנולוגיה או שפת תיכנות שהוא רוצה (בלי להעמיס על השרת).

אם אתה רוצה אפשר פשוט ללכת על ארכיטקטורה של Multiprocess שבה אתה יוצר תהליך שמתקשר אם התהליך הראשי בipc, אז אתה אמור להיות מסוגל להשתמש בטכנולוגיות מבוססות שמגובות על ידי חברות גדולות (ולא איזה פרוייקט צדדי של איזה מתכנת ), כל מיני access control frameworks כמו selinux,apparmor או firejail (מה שflatpak משתמש בו).

או שאתה ייכול לבדוק את הכיוון של DSL , יש שפות שדיי נוצרו בשביל הרחבות של תוכנה אז אולי יש כלים שמגבילים אותם (שפות כמו scheme,lua או TCL).
 

user32

Well-known member
מנהל
הדוגמה של הדו"ח זה רק פעולה אחת. פעולה נוספת למשל זה API שלנו שמאפשר סימון דאטה מסויים שמגיע לתוך המערכת שלנו. אנחנו מספקים מקום להכניס סקריפט שהאינפוט שלו זה אובייקטים עם מבנה די גדול שמגיעים ממקור כלשהו, הלקוח יכול לפי האינפוט להחזיר רשימה של attributes שיספקו לנו תובנות מסויימות.
תחשוב למשל על מקרה שלקוח רוצה להוסיף וולידציה ולסמן יישות "בעייתית". אצל אחד זה יכול להיות שהמשקל גדול ב2 ק"ג, אצל אחר זה אובייקט שיש לו מעל 12 בנים, אצל לקוח שלישי זה אובייקט שנמצא בהמתנה יותר מ3 ימים וכו'. אין שום דרך לספק UI שיאפשר להגדיר וולידציות כאלה חוץ מאשר לפתח כלי דמוי IDE (יש כאלה למשל בכלי ETL) שזה כמובן לא בסדר גודל הגיוני לפיתוח פונקציית וולידציה קטנה מקוסטמת. הרעיון הוא שנספק פלאגין שהלקוח יוכל לכתוב נוסחה שתחזיר invalid/valid. יש גם פלאגין אחר שמאפשר להוסיף attributes מסויימים לאובייקטים. בקיצור, הלכתי על הפתרון של הרצה בvm נפרד לפי הבלוג פוסט שהעליתי למעלה. כרגע רק אנחנו מעלים לשם סקריפטים ואם בעתיד נרצה נוכל להריץ את זה אפילו בפרוסס נפרד עם הגבלות ברמת מערכת ההפעלה.
 

Nuke1985

Active member
הם לא ייכולים להשחית את בסיס הנתונים במקרה הזה? (נניח למחוק בטעות מלא אובייקטים), מה תעשה אם זה יקרה? איבוד כל המידע לפני הגיבויי והשבתה עד שהמידע משוחזר?

מה אם לדבג? אתה אמור לדבג על בסיס נתונים שהוא בפרודקשן?

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

או שאפשר לפתח api מיוחד שיבדוק הרשאות לפני שהוא פועל (משהו כמו firefox addon api).

אם אתה מחפש עוד פרוייקט קוד פתוח לשאוב ממנו רעיונות אני חושב של gitlab גם יש api עם rate limit ,זה נראה דומה לapi של github שנראה שמאפשר לך לעשות הרבה דברים (לדוגמה לשרטט burndown chart של פרוייקט).
 

user32

Well-known member
מנהל
קודם כל, לא ברורה לי השאלה שלך. האם הם לא יכולים להשחית את בסיס הנתונים? זה בדיוק המצב שאני רוצה למנוע. אחרת לא הייתי מעלה את הנושא.
בעקרון להגיע לDB יהיה די קשה. הם מעלים סקריפט אבל אין להם נתוני התחברות לDB אז איך בדיוק יתחברו אליו? הסכנה היא בעיקר שינסו לקרוא לפונקציות שרצות באותו vm. לא שאנחנו מספקים להם את השמות של הפונקציות אבל נגיד שהם יצליחו לנחש איכשהו ולהריץ.

הפתרון להריץ בvm נפרד עם מגבלות מספק את ההגנה הכי ראויה בעיניי. זה context נפרד, אין גישה לפונקציות האחרות של המערכת וממילא גם לא לבסיס נתונים. והחבר'ה שכתבו את runInNewContext דאגו לחסום את הספריות והאובייקטים של JS שעלולות להוות סכנה: console, system וכו' וגם לאפשר מבחוץ להגדיר timeouts.

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

בכל מקרה, כרגע יישמתי את זה ככה.
 

Nuke1985

Active member
תהיתי האם הפתרון שיישמת מונע את זה, לא התכווני ללהשחית את הבסיס נתונים יישירות (אולי הייתי צריך במושג datamodel), אלא דרך שימוש באיזה API. לדוגמה ככה בסיס נתונים ייכול להיות מושחת דיי בקלות ובטעות:
JavaScript:
var productList = [{name: "orange"},{name: "apple"}]

for (let product of productList){
    if(product.name = "apple"){
        //do something
    }
    console.log(product.name);
}

console.log(productList)

אני אומנם לא מומחה לאקסל אבל לא נראה לי שנוסחת אקסל ייכולה להשחית תאים אחרים בקובץ.

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

user32

Well-known member
מנהל
אם קראת את הלינק שצירפתי אז התשובה היא כן, הוא מונע. הקוד רץ בcontext נפרד. אפשר להעביר לו אובייקטים "מבחוץ" אבל אם הוא ישנה אותם זה לא ישפיע על הcontext החיצוני. מה שכן אפשר לעשות זה להחזיר ערכים ואז מי שקורא לפונקציה מבחוץ יכול להחליט מה לעשות עם אותם ערכים. זה דומה להעברת פרמטרים בשפות שונות by value לעומת by reference למי שמכיר.

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