Can Machines Dream of Secure Code?
אבטחת קוד עם AI לא נגמרת ב-copy-paste. לירן טל מדגים NoSQL injection ו-XSS, ומראה למה הצעה של Copilot עדיין דורשת בדיקה, הקשר, review ו-test אמיתי.
צפו בהרצאההסיפור שמאחורי ההרצאה
- חולשת אבטחה יכולה להסתתר בקוד שנראה תמים, במיוחד כשה-input משנה משמעות של query.
- NoSQL injection מאפשר לעקוף אימות כשמעבירים אובייקט לא מהימן ישירות ל-MongoDB.
- ChatGPT ו-Copilot מייצרים קוד שימושי, אבל גם hallucinations ופתרונות חלקיים לאבטחה.
- escape HTML לא תמיד מספיק - ההגנה תלויה בהקשר שבו ה-input נכנס ל-DOM.
- כלי ניתוח קוד עוזרים לזהות זרימת data מסוכנת, אך אינם מחליפים הבנה ובדיקה.
אבטחת קוד עם AI לא נשברת רק כשמודל ממציא API. היא נשברת כשקוד שנראה סביר מקבל trust לפני שמישהו בדק איפה הוא רץ, איזה input מגיע אליו ומה הוא עושה ב-DOM או ב-database. לירן טל לוקח דוגמאות שנראות כמעט בנאליות - login מול MongoDB, השלמה של Copilot, HTML escaping - ומראה איך הפרט הקטן שמשאירים מחוץ ל-review הוא בדיוק המקום שבו מתחילה הפרצה.
זו אינה הרצאה נגד ChatGPT או GitHub Copilot. לירן משתמש בשניהם. אבל הוא מסרב לטעות הנוחה: להתייחס להצעה אוטומטית כאל קוד שעבר ביקורת. בעולם של dependencies, קוד מ-Stack Overflow ו-AI autocomplete, "הקוד שלי" כולל הרבה יותר מקבצים שכתבנו ביד. האחריות ההנדסית נשארת אצל מי שמכניס אותו לפרודקשן.
קוד פגיע לא תמיד נראה כמו קוד פגיע
לירן פותח בשורת קוד שיכולה להפוך ל-infinite loop רק בסביבה מסוימת, למשל כשהיא רצה על microcontroller עם מגבלת integer שונה. זה לא שיעור על לולאות. זו מסגרת חשיבה: פגיעות אינן תמיד משהו שמזהים במבט ראשון. הקשר, input, platform ו-assumptions קובעים אם קוד תמים הוא רק קוד או בעיה.
אותה מסגרת חוזרת בדוגמת login. יש username, password וקריאה שנראית ישירה ל-user.find. אפשר לזהות בעיות ברורות כמו סיסמה ב-clear text, אבל הבעיה המעניינת יותר נמצאת בעצם ההעברה של גוף הבקשה למודל של MongoDB. אם היישום מניח שה-password הוא string, אבל מקבל object, MongoDB עשוי לפרש אותו כאופרטור.
NoSQL injection: כשהסיסמה משנה משמעות
בדמו, ניסיון להתחבר עם סיסמה שגויה מחזיר 401. ואז במקום לשלוח סיסמה, לירן שולח object עם תנאי של MongoDB - למשל ערך שגדול מכל דבר. ה-query כבר לא שואל "האם הסיסמה שווה לערך הזה?" אלא מקבל תנאי אחר לחלוטין. התוצאה היא redirect לחשבון admin בלי לדעת את הסיסמה.
זה NoSQL injection. אין כאן string concatenation של SQL, ולכן קל לחשוב שההגנה המוכרת לא רלוונטית. אבל הבעיה היא אותה בעיה עקרונית: user input מגיע ל-query עם משמעות שהמפתח לא התכוון לתת לו. לירן מזכיר גם את Rocket.Chat, פרויקט open source גדול שבו נמצאה חולשה מאותו סוג. קוד פתוח, קהילה גדולה והרבה עיניים אינם ערובה לכך שהפרצה תיתפס לפני שמישהו ינצל אותה.
התיקון מתחיל בהבנה של ה-data model וה-API: validation, casting ל-type המצופה, והפרדה בין input לבין query semantics. הכלי יכול לסמן את הזרימה, אבל לא יחליף את השאלה הבסיסית: איזה data נכנס כאן, ואיך השכבה הבאה מפרשת אותו?
ChatGPT ו-Copilot הם עוזרים, לא שומרי סף
הקוד שלנו כבר מזמן לא מורכב רק ממה שכתבנו מאפס. יש NPM packages, snippets, תשובות בפורומים ועכשיו גם השלמות של Copilot ותשובות של ChatGPT. שני האחרונים מהירים, נוחים ולעיתים מרשימים מאוד. אבל הם מודלים סטטיסטיים. הם לא מבינים את המערכת כמו מי שמחזיק את המוצר, ולכן יכולים להישמע בטוחים גם כשהם מחזירים עובדה שגויה או פתרון חלקי.
זה מה שלירן מכנה hallucinations: המודל מחפש המשך שנראה מתאים, לא מבצע בהכרח את הבדיקה העובדתית או ההוכחה שאתם מדמיינים. בקוד, ההשלכה קשה יותר מתשובה מספרית שגויה. פונקציה יכולה להיראות נקייה, לעבור את ה-demo, ואפילו להתמודד עם case רגיל - ובכל זאת לפתוח injection, command execution או XSS בפינה שלא שאלתם עליה.
הדרך לעבוד עם AI היא לא להפסיק להשתמש בו. היא להכניס אותו לתהליך הנכון: לבקש הסבר, לקרוא את ההצעה, לבדוק assumptions, לכתוב tests ולתת review אנושי. פרומפט טוב יכול לשפר את הקוד שמתקבל, אבל הוא לא מסיר את הצורך לדעת מה נחשב input מסוכן ומהו sink רגיש.
XSS: ה-escape שעובד עד שהוא לא
בדמו השני לירן בונה אפליקציית React קטנה שמציגה data מ-API. אחד השדות נכנס דרך dangerouslySetInnerHTML. השם כבר מסמן את ה-tradeoff: אם description נשלט על ידי משתמש, הוא יכול לסגור תגית, להזריק element אחר ולהפעיל event handler. alert קטן מוכיח שהקוד רץ. תוקף אמיתי יכול לגנוב data מ-local storage, tokens או לבצע בקשות בשם המשתמש.
השלמה של Copilot מציעה פונקציית escapeHtml. בדוגמה הפשוטה היא נראית עובדת: ה-alert כבר לא קופץ והתוים הופכים ל-entities. ואז לירן משנה את ההקשר. הערך נכנס ל-HTML attribute ולא לטקסט רגיל. פתאום whitespace או event handler יכולים לשנות את המבנה, וה-escaping הקודם כבר לא מספיק.
זו הנקודה החשובה באבטחת קוד: אין פונקציה אחת שמתקנת "XSS". יש output encoding שמתאים ל-context, ויש הבחירה הבטוחה יותר - לא להזריק HTML לא מהימן מלכתחילה. מי שמעתיק helper מהצעת AI בלי להבין את נקודת השימוש מקבל ביטחון מזויף, לא הגנה.
להכניס אבטחה ל-workflow
לירן אינו מציע לסרוק קוד פעם אחת לפני release ולסמן וי. אבטחה צריכה להיות חלק מה-workflow: להכיר את ה-dependencies, לשאול מהו ה-input, לזהות sinks כמו query או HTML, ולבדוק את הנתיב ביניהם. static analysis ו-taint analysis יכולים לעקוב אחרי זרימת data ולהראות כשמידע לא מהימן מגיע לפונקציה רגישה. extension בעורך יכול להחזיר את ה-wiggly line מוקדם, כשהתיקון עוד קטן.
אבל כלי לא מחליף שיקול דעת. גם כשהכלי לא מזהה בעיה, צריך להבין את ה-protocol, את ה-type ואת ה-context. וגם כשהוא מזהה משהו, צריך לדעת אם זו חולשה ממשית ואיך לתקן בלי לשבור התנהגות. להעמקה באבטחת שרשרת האספקה ובקוד פתוח, האזינו גם ל-פרק 91 עם לירן טל.
ארבע שאלות לפני שמקבלים קוד שנוצר ב-AI
לפני שמכניסים snippet ל-repo, כדאי לעצור על ארבע שאלות. מאיפה מגיע ה-data? לאן הוא זורם? איך היעד מפרש אותו? ואיזה test מוכיח שההגנה עובדת גם מחוץ ל-happy path? ב-login זו יכולה להיות השאלה אם password הוא באמת string. ב-React זו השאלה אם הטקסט נכנס כטקסט, כ-attribute או כ-HTML. ב-command זו השאלה האם פרמטר מגיע ל-shell.
אין צורך להפוך כל משימת פיתוח לתרגיל אבטחה של שבוע. כן צריך להפסיק להתייחס ל-code suggestion כאל תשובה. בקשו מה-AI להסביר את ה-tradeoffs, נסו input לא צפוי, והריצו את הכלי הסטטי. אם אתם לא מבינים תיקון שמוצע לכם, עוד לא יודעים אם הוא מתקן או מסתיר את הבעיה.
השורה התחתונה: AI יכול להאיץ כתיבה. הוא לא יכול לקחת בעלות על security. קוד שנוצר אוטומטית ראוי לאותה רמת חשד, review ובדיקות כמו קוד שהגיע מכל מקור אחר - ולפעמים ליותר.
"דברים יכולים לקרות בצורה מאוד מאוד לא צפויה."
- לירן טל
"הקוד שלנו מורכב הרבה כבר מכל מיני חבילות קוד פתוח, לא באמת שלנו."
- לירן טל
"לא תמיד מעולה בלתת את מה שנכון."
- לירן טל
"צריך להבין את כל הניוואנסים הקטנים שקורים שם"
- לירן טל
- 00:00:00 קוד תמים, תוצאה לא צפויהלירן פותח בדוגמאות שמראות למה שורת קוד שנראית תקינה יכולה להישבר בסביבה או בהקשר שלא ציפינו להם.
- 00:04:50 NoSQL injection ב-loginדמו של MongoDB מראה איך input שאינו string משנה את משמעות השאילתה ועוקף בדיקת סיסמה.
- 00:10:16 גם קוד פתוח גדול מפספסהחולשה אינה רק תרגיל: לירן מציג מקרה דומה ב-Rocket.Chat ואת המחיר של endpoint לא מאומת.
- 00:12:58 הקוד שלנו כבר לא רק שלנוDependencies, Stack Overflow, ChatGPT ו-GitHub Copilot מרחיבים את מקורות הקוד ואת שטח האמון.
- 00:16:58 Hallucinations וקוד שנשמע בטוחLLM יכול להישמע משכנע גם כשהוא מחזיר עובדה שגויה או השלמה שלא פותרת את בעיית האבטחה.
- 00:21:40 הצעות AI ל-SQL ול-command injectionלירן בודק קוד שמציעים ChatGPT ו-Copilot ומראה למה הפרומפט וההקשר משפיעים על הסיכון.
- 00:28:40 XSS בתוך אפליקציית Reactdata שמוזרם ל-dangerouslySetInnerHTML מאפשר להזריק HTML ו-JavaScript כשהוא לא מטופל נכון.
- 00:35:48 למה escape HTML אינו תיקון אוניברסליהדמו מראה ש-escaping שעובד לטקסט נכשל כשהערך נכנס ל-HTML attribute, ולכן צריך output encoding לפי הקשר.
- 00:40:30 כלים, awareness ובדיקת codeהסיום מחבר בין ידע על ה-sink, ניתוח זרימת data וכלים שמסמנים insecure code כבר בזמן הפיתוח.
מה כוסה בהרצאה
שאלות מההרצאה
מה זה NoSQL injection ואיך הוא עוקף login?
זו חולשה שבה input לא מהימן מגיע ל-query בלי type checking או sanitization מתאימים. בדמו של לירן, במקום סיסמה כ-string אפשר לשלוח object עם אופרטור של MongoDB. ה-query מקבל משמעות אחרת, בודק תנאי רחב יותר ומאפשר לעקוף את הסיסמה.
האם אפשר לסמוך על קוד מ-ChatGPT או GitHub Copilot?
אפשר להשתמש בו כנקודת התחלה, לא כחותמת אבטחה. לירן מדגיש ש-LLM הוא מודל סטטיסטי שעלול להחזיר hallucinations או השלמה שנראית סבירה אך מפספסת ניואנס. קוראים את הקוד, בודקים את ההקשר ומריצים בדיקות לפני שמכניסים אותו למוצר.
למה escape HTML לא תמיד מונע XSS?
ההגנה תלויה ב-context. escaping שעשוי למנוע הזרקה לטקסט יכול להיכשל אם הערך מוזן לתוך attribute של HTML, שבו רווח, quote או event handler משנים את המבנה. צריך output encoding שמתאים למקום המדויק שבו ה-data נכנס, ועדיף להימנע מהזרקת HTML לא מהימן.
איך מזהים insecure code לפני production?
מתחילים ב-awareness: יודעים לזהות user input שמגיע ל-sink רגיש, למשל query או HTML. אחר כך מוסיפים code review, tests וכלי static analysis או taint analysis שמזהים זרימה כזאת. לירן מציין כלים שמסמנים בעיות בעורך, אך הכלי לא פוטר מהבנת המערכת וה-dependency שבה משתמשים.