feat(mcp): שער Bearer לתחבורת ה-HTTP (#231.2) #426

Merged
chaim merged 2 commits from worktree-mcp-http-auth into main 2026-08-05 08:16:58 +00:00
Owner

למה

ב-stdio ההגנה היא הצינור עצמו — רק תהליך שכבר רץ כמשתמש הזה יכול לדבר עם השרת. streamable-http מבטל את התכונה הזו לחלוטין: כל מי שמגיע ל-socket יכול לקרוא לכל אחד מ-108 הכלים, ובמרשם יש case_delete, precedent_library_delete, document_upload וכל כלי כתיבת-הבלוקים.

מאזין לא-מאומת הוא, הלכה למעשה, endpoint למחיקת תיקים. זהו החסם שנקבע ב-#231.1 ככזה שחייב לנחות לפני חשיפה כלשהי.

שלב 2 מתוך 5 ב-#231.

מה

מימוש דרך ה-TokenVerifier של ה-SDK ו-BearerAuthBackend שלו — לא middleware משלנו. מסלול-אימות אחד, של המסגרת (G2).

ההחלטות שקובעות את בטיחות הפיצ'ר:

  • מסרב לעלות בלי טוקן.MissingTokenError קטלנית. החלופה המפתה — לעלות ולרשום warning — מייצרת מאזין שנראה בריא ועונה על כל קריאה הרסנית. סירוב-אתחול הוא הכשל הבטוח (§6).
  • הבנייה בזמן-import ולא בתוך main(): ‏FastMCP מקבל token_verifier ו-auth כארגומנטי-בנאי, ולכן טוקן חסר חייב להיכשל לפני שה-listener קיים — לא אחרי שהוא כבר מקבל חיבורים.
  • stdio לא נוגע. דרישת טוקן שם הייתה שוברת כל סשן אינטראקטיבי בלי שום רווח אבטחתי.
  • השוואה בזמן-קבוע (hmac.compare_digest) — ‏== נאיבי מדליף את הטוקן בייט-בייט לתוקף שמודד זמנים.
  • verify_token מחזיר None ולא זורק — זה אות ה"דחייה" של הפרוטוקול, ומניב 401 נקי במקום 500 שנקרא ככשל-שרת.
  • סף אורך 32 תווים — טוקן קצר נדחה באתחול ולא מתגלה מ-access log.
  • הטוקן נקרא מ-env (יאוכלס מ-Infisical), לא מוטמע בקוד, ולא נרשם ללוג.

אימות — הרצה חיה

בדיקה תוצאה
HTTP בלי טוקן סירוב לעלות, exit 1
stdio בלי טוקן עולה כרגיל, auth כבוי
POST בלי Authorization 401
POST עם טוקן שגוי 401
POST עם הטוקן הנכון 200
claude דרך HTTP+Bearer 108 כלים · mcp__legal-ai__case_get
מופעי הטוקן בלוג 0

Invariants

  • G2 — מקיים: מסלול-אימות יחיד, של ה-SDK; לא מנגנון מקביל.
  • G12 — מקיים: המודול נקי מסמלי-פלטפורמה; leak_guard ירוק.
  • INV-AG3 — לא נגוע; agent_tool_grants_guard ירוק.

מה עוד חסר לפני שזה שמיש

  1. הסוד ב-Infisical — הקוד קורא MCP_HTTP_TOKEN, הסוד עצמו טרם נוצר (טעון _GUIDELINES/SAVE_SECRET_RULES ואישור-יו"ר).
  2. שירות pm2 — אין עדיין; השרת רץ רק בהרצות-מבחן.

שניהם ב-#231.3.

## למה ב-`stdio` ההגנה היא הצינור עצמו — רק תהליך שכבר רץ כמשתמש הזה יכול לדבר עם השרת. **`streamable-http` מבטל את התכונה הזו לחלוטין:** כל מי שמגיע ל-socket יכול לקרוא לכל אחד מ-108 הכלים, ובמרשם יש `case_delete`, `precedent_library_delete`, `document_upload` וכל כלי כתיבת-הבלוקים. **מאזין לא-מאומת הוא, הלכה למעשה, endpoint למחיקת תיקים.** זהו החסם שנקבע ב-#231.1 ככזה שחייב לנחות לפני חשיפה כלשהי. שלב 2 מתוך 5 ב-#231. ## מה מימוש דרך ה-`TokenVerifier` של ה-SDK ו-`BearerAuthBackend` שלו — **לא middleware משלנו**. מסלול-אימות אחד, של המסגרת (G2). **ההחלטות שקובעות את בטיחות הפיצ'ר:** - **מסרב לעלות בלי טוקן.** ‏`MissingTokenError` קטלנית. החלופה המפתה — לעלות ולרשום warning — מייצרת מאזין שנראה בריא ועונה על כל קריאה הרסנית. סירוב-אתחול הוא הכשל הבטוח (§6). - **הבנייה בזמן-import** ולא בתוך `main()`: ‏FastMCP מקבל `token_verifier` ו-`auth` כארגומנטי-בנאי, ולכן טוקן חסר חייב להיכשל **לפני** שה-listener קיים — לא אחרי שהוא כבר מקבל חיבורים. - **`stdio` לא נוגע.** דרישת טוקן שם הייתה שוברת כל סשן אינטראקטיבי בלי שום רווח אבטחתי. - **השוואה בזמן-קבוע** (`hmac.compare_digest`) — ‏`==` נאיבי מדליף את הטוקן בייט-בייט לתוקף שמודד זמנים. - **`verify_token` מחזיר `None` ולא זורק** — זה אות ה"דחייה" של הפרוטוקול, ומניב 401 נקי במקום 500 שנקרא ככשל-שרת. - **סף אורך 32 תווים** — טוקן קצר נדחה באתחול ולא מתגלה מ-access log. - הטוקן נקרא מ-env (יאוכלס מ-Infisical), לא מוטמע בקוד, ולא נרשם ללוג. ## אימות — הרצה חיה | בדיקה | תוצאה | |---|---| | HTTP בלי טוקן | **סירוב לעלות**, exit 1 | | stdio בלי טוקן | עולה כרגיל, auth כבוי | | `POST` בלי `Authorization` | **401** | | `POST` עם טוקן שגוי | **401** | | `POST` עם הטוקן הנכון | **200** | | claude דרך HTTP+Bearer | **108 כלים** · `mcp__legal-ai__case_get` | | מופעי הטוקן בלוג | **0** | ## Invariants - **G2** — מקיים: מסלול-אימות יחיד, של ה-SDK; לא מנגנון מקביל. - **G12** — מקיים: המודול נקי מסמלי-פלטפורמה; `leak_guard` ירוק. - **INV-AG3** — לא נגוע; `agent_tool_grants_guard` ירוק. ## מה עוד חסר לפני שזה שמיש 1. **הסוד ב-Infisical** — הקוד קורא `MCP_HTTP_TOKEN`, הסוד עצמו טרם נוצר (טעון `_GUIDELINES/SAVE_SECRET_RULES` ואישור-יו"ר). 2. **שירות pm2** — אין עדיין; השרת רץ רק בהרצות-מבחן. שניהם ב-#231.3.
chaim added 1 commit 2026-08-05 07:53:07 +00:00
feat(mcp): שער Bearer לתחבורת ה-HTTP — מסרב לעלות בלי טוקן (#231.2)
All checks were successful
Lint — undefined names / undefined-names (pull_request) Successful in 11s
INV-AG3 Agent Tool Grants / agent-tool-grants (pull_request) Successful in 4s
G12 Leak-Guard / leak-guard (pull_request) Successful in 5s
eebb193fd3
ב-stdio ההגנה היא הצינור עצמו: רק תהליך שכבר רץ כמשתמש הזה יכול לדבר עם
השרת. streamable-http מבטל את התכונה הזו לחלוטין — כל מי שמגיע ל-socket
יכול לקרוא לכל אחד מ-108 הכלים, ובמרשם יש case_delete,
precedent_library_delete, document_upload וכל כלי כתיבת-הבלוקים. מאזין
לא-מאומת הוא, הלכה למעשה, endpoint למחיקת תיקים.

מימוש דרך ה-TokenVerifier של ה-SDK ו-BearerAuthBackend שלו — לא middleware
משלנו. מסלול-אימות אחד, של המסגרת (G2).

ההחלטות שקובעות את בטיחות הפיצ'ר:
- **מסרב לעלות בלי טוקן.** MissingTokenError קטלנית. החלופה המפתה — לעלות
  ולרשום warning — מייצרת מאזין שנראה בריא ועונה על כל קריאה הרסנית.
  סירוב-אתחול הוא הכשל הבטוח (§6).
- הבנייה בזמן-import ולא בתוך main(): FastMCP מקבל token_verifier ו-auth
  כארגומנטי-בנאי, ולכן טוקן חסר חייב להיכשל *לפני* שה-listener קיים.
- **stdio לא נוגע.** דרישת טוקן שם הייתה שוברת כל סשן אינטראקטיבי בלי שום
  רווח אבטחתי — הגבול שם הוא הצינור.
- השוואה בזמן-קבוע (hmac.compare_digest); == נאיבי מדליף את הטוקן בייט-בייט
  לתוקף שמודד זמנים.
- verify_token מחזיר None ולא זורק — זה אות ה"דחייה" של הפרוטוקול ומניב 401
  נקי במקום 500 שנקרא ככשל-שרת.
- סף אורך 32 תווים; טוקן קצר נדחה באתחול ולא מתגלה מ-access log.
- הטוקן נקרא מ-env (שיאוכלס מ-Infisical), לא מוטמע, ולא נרשם ללוג.

אומת בהרצה חיה:
  HTTP בלי טוקן → סירוב לעלות, exit 1
  stdio בלי טוקן → עולה כרגיל, auth כבוי
  POST בלי Authorization        → 401
  POST עם טוקן שגוי             → 401
  POST עם הטוקן הנכון           → 200
  claude דרך HTTP+Bearer        → 108 כלים, mcp__legal-ai__case_get
  מופעי הטוקן בלוג              → 0

invariants: G2 — מקיים (מסלול-אימות יחיד, של ה-SDK). G12 — מקיים; המודול
נקי מסמלי-פלטפורמה, leak_guard ירוק. INV-AG3 — לא נגוע, השער ירוק.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
chaim added 1 commit 2026-08-05 08:15:34 +00:00
refactor(mcp): MCP_HTTP_TOKEN → MCP_HTTP_SHARED_SECRET — זהה לשם ב-Infisical
All checks were successful
Lint — undefined names / undefined-names (pull_request) Successful in 11s
INV-AG3 Agent Tool Grants / agent-tool-grants (pull_request) Successful in 5s
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
363485d025
הסוד נוצר ב-All Infrastructure / env main / /apps/legal-ai בשם
MCP_HTTP_SHARED_SECRET, מתויג credentials, לפי הדפוס של שני טוקני-הגשר
שכבר שם: COURT_FETCH_SHARED_SECRET ו-LEGAL_CHAT_SHARED_SECRET.

שם זהה בקוד וב-Infisical = אין מיפוי שצריך לזכור.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
chaim merged commit c22935c008 into main 2026-08-05 08:16:58 +00:00
chaim deleted branch worktree-mcp-http-auth 2026-08-05 08:16:58 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ezer-mishpati/legal-ai#426