שומרים ואינטרספטורים של אבטחה
מערכת BT Management משתמשת במספר Guards ו-Interceptors גלובליים כדי לאכוף אבטחה, אימות והרשאות בכל בקשה ל-API.
סדר ביצוע השומרים (Guards Execution Order)
כל בקשת HTTP עוברת דרך השומרים הבאים לפי הסדר:
1. JwtAuthGuard
תפקיד: אימות אסימון (JWT Token).
- מה הוא עושה:
- מחלץ את ה-
Bearer Tokenמכותרת ה-Authorization. - מאמת את חתימת האסימון באמצעות
JWT_SECRET. - מפענח את ה-Payload ומזרים את
userל-request.user.
- מחלץ את ה-
- דילוג:
- אם הנקודת הקצה (Endpoint) מסומנת עם
@Public(), השומר מדולג.
- אם הנקודת הקצה (Endpoint) מסומנת עם
- מיקום הקוד:
src/auth/guards/jwt-auth.guard.ts
2. PermissionsGuard
תפקיד: בדיקת הרשאות משתמש.
- מה הוא עושה:
- קורא את הדקורטור
@Permissions()מנקודת הקצה. - בודק אם למשתמש יש את ההרשאות הנדרשות (
request.user.permissions). - מאפשר גישה אוטומטית למשתמשים עם
isAdmin: true.
- קורא את הדקורטור
- דילוג:
- אם הנקודת הקצה ציבורית (
@Public()). - אם אין דקורטור
@Permissions()(מניח שאין דרישה).
- אם הנקודת הקצה ציבורית (
- מיקום הקוד:
src/common/guards/permissions.guard.ts
3. RateLimitGuard
תפקיד: הגנה מפני התקפות DoS ושימוש יתר.
- מה הוא עושה:
- מגביל את מספר הבקשות למשתמש או כתובת IP בפרק זמן מסוים.
- משתמש ב-Redis לשמירת מונים.
- מחזיר
429 Too Many Requestsאם חרגו מהגבול.
- הגדרה:
- ברירת מחדל: 100 בקשות לדקה למשתמש.
- ניתן להתאמה אישית לפי נקודת קצה באמצעות
@RateLimit()decorator.
- מיקום הקוד:
src/common/guards/rate-limit.guard.ts
4. AccountLockoutGuard
תפקיד: נעילת חשבונות לאחר ניסיונות התחברות כושלים.
- מה הוא עושה:
- בודק אם החשבון נעול (
user.isLocked: true). - מחזיר
403 Forbiddenאם החשבון נעול. - המערכת נועלת חשבון אוטומטית לאחר 5 ניסיונות כושלים.
- בודק אם החשבון נעול (
- שחרור נעילה:
- אוטומטי לאחר 30 דקות.
- ידני על ידי מנהל מערכת.
- מיקום הקוד:
src/common/guards/account-lockout.guard.ts
סכימת זרימה
Client Request
↓
[JwtAuthGuard] ← אימות אסימון
↓
[PermissionsGuard] ← בדיקת הרשאות
↓
[RateLimitGuard] ← הגבלת קצב
↓
[AccountLockoutGuard] ← בדיקת נעילה
↓
[TenantInterceptor] ← הגדרת הקשר דייר (ALS)
↓
Controller Method ← קריאה למתודה
↓
[TransformInterceptor] ← עיטוף תגובה
↓
Client Response
אינטרספטורים (Interceptors)
אינטרספטורים רצים לאחר השומרים ולפני/אחרי המתודה של הקונטרולר.
TenantInterceptor
תפקיד: הגדרת הקשר דייר (Tenant Context) באמצעות ALS.
- מה הוא עושה:
- מחלץ את
userIdמתוךrequest.user. - בודק אם יש כותרת
x-customer-context(התחזות מנהל). - מגדיר את ה-Store של
AlsServiceעם{ userId, isAdmin }.
- מחלץ את
- חשיבות:
- זה הבסיס למערכת Multi-Tenancy.
- כל שאילתות Prisma מסתמכות על זה לסינון אוטומטי.
- מיקום הקוד:
src/common/interceptors/tenant.interceptor.ts
TransformInterceptor
תפקיד: עיטוף תגובות במבנה אחיד.
- מה הוא עושה:
- עוטף כל תגובה בפורמט:
{
"success": true,
"data": { ... },
"count": 10
} - מוסיף
countאם התוצאה היא מערך.
- עוטף כל תגובה בפורמט:
- דילוג:
- אם הנקודת הקצה מסומנת עם
@PlainResponse()(למשל: הורדת קבצים).
- אם הנקודת הקצה מסומנת עם
- מיקום הקוד:
src/common/interceptors/transform.interceptor.ts
דקורטורים מותאמים אישית
@Public()
שימוש: מסמן נקודת קצה כציבורית (ללא אימות).
@Public()
@Get('health')
getHealth() {
return { status: 'ok' };
}
@Permissions(...)
שימוש: דורש הרשאות ספציפיות.
@Permissions(Permission.MANAGE_DEVICES)
@Delete(':id')
deleteDevice(@Param('id') id: string) {
// ...
}
@PlainResponse()
שימוש: מדלג על TransformInterceptor (לתגובות גולמיות).
@PlainResponse()
@Get('download/:id')
downloadFile(@Param('id') id: string, @Res() res: Response) {
return res.download('path/to/file');
}
@RateLimit(limit, window)
שימוש: הגדרת הגבלת קצב מותאמת אישית.
@RateLimit(10, 60) // 10 בקשות לדקה
@Post('send-message')
sendMessage() {
// ...
}
בדיקה ופיתוח
דילוג על אבטחה בפיתוח
ב-.env פיתוח:
NODE_ENV=development
SKIP_AUTH=true # זהירות! השתמש רק לוקאלית
זה מאפשר דילוג על JwtAuthGuard לצורך בדיקה מהירה.
בדיקת Guards ידנית
// בקובץ בדיקה (E2E)
it('should return 401 without token', async () => {
const response = await request(app.getHttpServer())
.get('/devices')
.expect(401);
expect(response.body.message).toContain('Unauthorized');
});
שיטות עבודה מומלצות
- אל תדלג על Guards אלא אם הנקודת קצה באמת ציבורית.
- השתמש ב-@Permissions תמיד כשיש צורך בהרשאה.
- אל תשכח @PlainResponse בהורדות קבצים (למניעת עיטוף כפול).
- נטר Rate Limits ב-Redis כדי לזהות שימוש חריג.
- בדוק AccountLockout תקופתית כדי לשחרר משתמשים תקינים.
פתרון תקלות
שגיאה: "Unauthorized" למרות אסימון תקין
סיבות:
- האסימון פג תוקף.
JWT_SECRETשגוי או השתנה.- השרת הופעל מחדש ו-Redis נוקה.
פתרון:
# בדוק תוקף האסימון
jwt decode <your-token>
# וודא ש-JWT_SECRET תואם
echo $JWT_SECRET
שגיאה: "403 Forbidden"
סיבות:
- למשתמש אין את ההרשאה הנדרשת.
- החשבון נעול.
פתרון:
// בדוק בDB
db.user.findUnique({ where: { id: userId } });
// אם isLocked: true, שחרר ידנית או המתן 30 דקות.
שגיאה: "429 Too Many Requests"
סיבות:
- חרגת מהגבלת הקצב.
פתרון:
- המתן עד לאיפוס החלון (בדרך כלל דקה אחת).
- או הגדל את ה-Limit:
@RateLimit(200, 60)