ספריית Sequelize: איך למנוע שליחת שדות מסויימות לצד לקוח
-
לאובייקט שחושפים ללקוח קוראים DTO או ViewModel.
בC# מתמודדים עם זה על ידי אטריביוטים ש"מסתירים" מאפיינים נבחרים, או מחלקה נפרדת עם מילוי על ידי Auto Mapper.
במקרים רבים שליחה עיוורת של האובייקט ממסד הנתונים חושפת דברים שקשה לחשוב עליהם, כמו למשל כמות הלקוחות לפי הID רץ וכולי. -
שאלה דומה בסטאק:
https://stackoverflow.com/questions/27972271/sequelize-dont-return-password
אני חושב שזו דוגמה טובה לבלגן ה"איך" ששורר בnodejs.
בעוד בC# יש עומס מרגיז של שיטות וכללי עצוב, בנוד חסר מאוד כאלו.
אגב הרבה לא שמים לב למשהו שיותר מסוכן - הכיוון ההפוך של קבלה מלקוח,
כשמקבלים מהלקוח אובייקט יש סכנה יותר הפוכה שנקראת Mass Assignment או Overposting שזה מתי שהקוד בצד שרת מקבל אובייקט ולמשל מעדכן על פיו את מסד הנתונים בדינמיות, אך בעוד לקליינט מותר לערוך רק את כתובותו האישית, הוא מוסיף שדות שהוא בכלל לא אמור להיות יכול לשנות (את שמם הוא מנחש או יודע דרך הבעיה של @yossiz).
בexpress למשל, מצוי שרואים קוד בסגנון הזה:app.post('/update', async (req, res) => { if(req.body.id != req.seesion.user.id) return res.status(403); await connection.query('UPDATE users SET ? WHERE id = ?', [req.body, req.body.id]); ... });
הקוד הזה שהוא ממש קסום מבחינת פשטותו, הוא פרצת אבטחה בה משתמש יכול לשנות כל שדה, כולל את הID (ל1 למשל ואז הוא בטוח מנהל...)....
ראיתי מלא קוד דומה בפרודקשיין! יש פה טענה קצת על ספריית mysql שעשתה חיים מידי יפים בלי שום התרעה או אזהרה בתיעוד, אבל בnosql המצב הרבה יותר חמור.
בנוסף, בגלל שאין טיפוסיות קשוחה בJS המשתמש יכול לנצל את זה ולשלוח טיפוס שמצד אחד ייתמך במסד נתונים כדי שלא תהיה שגיאה בהכנסה או העדכון, אבל יהיו לכך השלכות לא טובות לגבי הלוגיקה בהמשך. אפילו מחלקה בנויה יפה בtypeScript לא תעזור פה שהרי בזמן ריצה הוא JS והוא מסכים הכל.
יש ספריות לזה, כמו express-validator אבל מי משתמש בה ביחס לכלל? זה אחת הבעיות של הטכנולוגיות המדליקות והחופשיות, אין שמה נורמות בסיסיות מרוב שרוצים לתת את ההגה למפתח.
מאמרים על Mass Assignment בנוד:
https://snyk.io/blog/avoiding-mass-assignment-node-js/
https://knowledge-base.secureflag.com/vulnerabilities/inadequate_input_validation/mass_assignment_nodejs.html -
@dovid כתב בספריית Sequelize: איך למנוע שליחת שדות מסויימות לצד לקוח:
כמו למשל כמות הלקוחות לפי הID רץ וכולי.
נו ו.. לכן לא לשלוח id איזה תחליף יכול להיות לו?
אם בתכלת כל הזיהוי של הלקוח הוא לפי ה id כי כל דבר יכול להתחלף (טלפון, מייל, ת.ז. וכו') או להיות כפול (במקרה שאין לי בעיה שיהיו 2 חשבונות ללקוח מסוים)
אז פשוט ליצור עוד id לייצר מחרוזת סתם? זה מכביד על הניהול.
ובפרט מי שמשתמש ב sql זה id רץ וכל ה joinים עושים לפי זה. -
@avi-rz לא אמרתי לא לשלוח ID, אמרתי לשים לב מתי לשלוח אותו.
יש המון ישויות שהצד לקוח לא מעדכן או שגם אם הוא מעדכן לא צריך בשביל זה ID ישיר.
לפעמים חייבים לעשות מה שאמרת ולהסתיר את הID על ידי עמודה נוספת של זיהוי GUID (פעם כתבתי על פתרונות אחרים https://tchumim.com/topic/13821/תרגיל-מתמטי-של-הסתרת-מזהה-רץ) -
@dovid כתב בספריית Sequelize: איך למנוע שליחת שדות מסויימות לצד לקוח:
app.post('/update', async (req, res) => {
if(req.body.id != req.seesion.user.id) return res.status(403);
await connection.query('UPDATE users SET ? WHERE id = ?', [req.body, req.body.id]);
...
});לא הבנתי למה זה פירצת אבטחה אתה הרי בודק לפני כן אם זה שווה ל seesion אז איך הלקוח יכול לשנות id?
-
@ivrtikshoret טכנית אתה צודק לגבי הקוד המדוייק ש@dovid הביא. אבל הנקודה הכללית עדיין עומדת וקיימת. כלומר ששם אי אפשר לשנות את שדה
id
ספציפית כי יש בדיקה לזה. גם בלי הבדיקה, הרי הרשומה שמעדכנים היא הרשומה עם ה-id
ששלחת ב-body, אז תמיד זה יהיה זהה. אבל עדיין ייתכן שיש שדות רגישות אחרות. כמו"כ בהרבה מקרים פרמטרid
שמצביע על המזהה של הרשומה שצריך לעדכן נשלח בנפרד לאובייקט העדכון ואז הבדיקה לא תועיל. -
@ivrtikshoret באמת שגיתי בכתיבה והתכוונתי למשפט האחרון של @yossiz,
אבל גם במקרה הזה הוא התוקף יכול לשלוח שני פרמטרים, אחד id בקטן תקין עם המזהה הנכון שלו, והשני ID עם המספר 1, הSQL לא רגיש לרישיות.
דוקא השאלה שלך מחדדת איך המתכנת יכול לפספס OVERPOSTING. -
@dovid בזה אתה צודק מאה אחוז,
מה שאני עושה בד"כ, אני לא מעביר את כל ה body, אלא שולף מה body רק את מה שאני רוצה שיוכלו לשלוח ל db -
@dovid כתב בספריית Sequelize: איך למנוע שליחת שדות מסויימות לצד לקוח:
אבל גם במקרה הזה הוא התוקף יכול לשלוח שני פרמטרים, אחד id בקטן תקין עם המזהה הנכון שלו, והשני ID עם המספר 1, הSQL לא רגיש לרישיות.
@yossiz העיר לי באישי שמשפט הsql שיווצר ייראה כזה:
SET ID = 1, id = 565, ...
בעוד בsql server זה מוציא שגיאה של עמודה כפולה, בmysql זה עובר בשקט - ההשמה האחרונה מנצחת. אז התוקף צריך לסדר נכון את האובייקט...