🎓 מפתחי בינה מלאכותית - GenAI Engineers

הפרדה בין צד לקוח וצד שרת, ושילוב APIs בין שירותים

234 שעות אקדמיות 2 סמסטרים E2E - קונספט עד ענן

אבטחת API - Security by Design

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

מטרה: להפוך API מ"עובד" ל"עמיד": פחות פריצות, פחות דליפות, פחות תקלות תפעוליות, והפתעות נעימות בריאיון עבודה.

1) מודל איומים קצר: מה התוקף מנסה לעשות?

2) Top mistakes בפרויקטים של API

Broken Authorization

המערכת “מאמתת” משתמש, אבל לא בודקת שהוא מורשה למשאב (IDOR). זה הבאג הכי יקר.

דוגמה: GET /users/123 מחזיר נתונים גם אם אתה משתמש 999.

Secrets בקוד

מפתחות JWT, API keys, credentials - לא נשמרים ב-repo. בפרודקשן רק ENV/Secret Manager.

No rate limiting

endpoint יקר בלי הגנה = הפקת עלויות והפלת שירות. במיוחד בעולמות GenAI.

Input לא מוגבל

קבצים ענקיים, JSON עצום, טקסטים אינסופיים - תוקף יכול להפיל שרת גם בלי “פריצה”.

3) שכבות הגנה: Defense in Depth

שכבה אחת לא מספיקה. הפתרון הטוב הוא שילוב:

4) Authentication מול Authorization - איפה אנשים נופלים?

אימות (JWT) אומר “מי אתה”. הרשאות אומרות “מה מותר לך”. רוב הפריצות קורות כשמסתמכים על user_id מה-path או מה-body בלי להשוות לזהות מה-token.

# לא נכון: סומכים על user_id מהלקוח
@app.get("/users/{user_id}")
def get_user(user_id: int):
    return db.get_user(user_id)

# נכון: מזהות מה-token + בדיקת הרשאה
@app.get("/me")
def me(user=Depends(get_current_user)):
    return db.get_user(user.user_id)

5) Rate limiting ו-Protecting expensive endpoints

מנגנון בסיסי: “כמה בקשות פר משתמש ליחידת זמן”. אפשר ליישם ב-Redis או לשים ברמת gateway.

דוגמה רעיונית עם Redis (קונספט)
# key: rl:{user_id}:{minute_bucket}
# INCR + EXPIRE
# אם עבר threshold - מחזירים 429 Too Many Requests

בפרודקשן נהוג להשתמש ב-Gateway/WAF או ספרייה מוכחת, לא להמציא לבד.

6) Input Validation + Schema hardening

from pydantic import BaseModel, ConfigDict, Field

class NoteIn(BaseModel):
    model_config = ConfigDict(extra="forbid")
    title: str = Field(min_length=1, max_length=120)
    content: str = Field(max_length=10_000)

7) CORS, Cookies ו-CSRF - מה צריך לדעת

CORS הוא מנגנון דפדפן שמגן על המשתמש, לא על השרת. אם אתה משתמש ב-cookies לאימות, אתה חייב לחשוב גם על CSRF. אם אתה משתמש ב-Bearer token ב-header, CSRF פחות רלוונטי, אבל עדיין צריך CORS מדויק.

כלל פשוט

  • API ל-SPA מודרני: לרוב Bearer tokens, CORS מגודר ל-origin שלך.
  • Cookies: דרוש CSRF token + SameSite + Secure.

8) File uploads: אבטחת העלאות וקבצים

קבצים הם שטח התקפה קלאסי. חשוב להפריד: Object storage לקבצים ו-DB למטא-דאטה.

9) Secrets management

סודות הם “דלת אחורית” אם דולפים. בפרודקשן: secrets ב-Secret Manager/ENV, הרשאות מינימליות, ורוטציה.

מה לא עושים

  • מפתח JWT ב-git
  • .env בתוך repo ציבורי
  • אותו סוד לכל סביבה

מה כן עושים

  • Secret per env (dev/stage/prod)
  • Rotation תקופתי
  • Audit מי ניגש לסוד

10) Logging, Audit trail, ופרטיות

11) Checklist קצר לפרודקשן

  • HTTPS בלבד
  • JWT/API keys עם TTL ורוטציה
  • Authorization לכל משאב (אין IDOR)
  • Rate limiting (במיוחד ל-LLM/RAG)
  • Request size limits
  • Secrets ב-Secret Manager
  • Logging זהיר + audit trail

תרגול

שאלות בדיקה (12)
  1. למה CORS לא “מאבטח את השרת”?
  2. מה זה IDOR ולמה הוא נפוץ?
  3. מתי CSRF רלוונטי ומתי פחות?
  4. מה ההבדל בין 401, 403, 429?
  5. למה חשוב להגביל request size?
  6. למה endpoints של GenAI נחשבים “יקרים” מבחינת אבטחה?
  7. מה הסיכון בהחזקת secrets בקוד?
  8. איזה מידע לא כדאי להכניס ללוגים?
  9. מה היתרון ב-Signed URLs לקבצים?
  10. למה “extra=forbid” יכול להציל אותך?
  11. מה ההבדל בין rate limiting ב-app לבין gateway?
  12. תן 2 שכבות הגנה שונות שהן לא אותה שכבה.
משימת בית (Hardening ל-CRUD שלך)

מטרה: להפוך את ה-CRUD שלך לעמיד יותר.

  • הוסף extra="forbid" לכל models.
  • הוסף הגבלת אורך לתוכן (Field max_length).
  • הגן על כל endpoint עם auth, וודא שאין IDOR (notes רק של המשתמש).
  • הוסף rate limit ל-login ול-endpoint היקר ביותר.
  • הוסף upload לקובץ: קובץ נשמר ב-storage (לתרגול אפשר “fake”), וב-DB נשמר רק URL.
בדיקת פתרון (Checklist)
  • אין endpoints שמקבלים user_id מהלקוח כדי לקבוע זהות.
  • כל משאב נבדק מול המשתמש המחובר (ownership או role).
  • קלט מוגבל וסכימה קשיחה (Pydantic).
  • קיימת הגנה על endpoints יקרים (rate limit).
  • הפרדת binary data מה-DB (URL/metadata בלבד).
  • אין secrets ב-repo.