איך להגן על מפתחות API בתוך סוכני AI
סוכני AI חשופים לדליפת מפתחות דרך prompt injection פשוט. אל תשתמשו במפתחות קבועים; למדו כיצד להגדיר שער אימות עם אסימונים לטווח קצר וגישה מוגבלת לכל כלי בנפרד.
מה המשמעות של הרחקת סודות מסוכני AI
סוכן AI הוא תהליך Linux רגיל שמריץ פקודות. כל משתנה סביבה (environment variable) שמוחזק על ידי התהליך קריא עבור הקוד שהוא מריץ; לכן, מפתח API שנמצא בסביבת הסוכן הוא מפתח שהסוכן יכול לשלוח לכל מארח שאליו הוא מסוגל להגיע. הרחקת סודות מהסוכן משמעותה מתן "ידית" (handle) במקום המפתח עצמו: אסימון (token) בעל טווח הרשאות מוגבל וזמן חיים קצר, או מציין מיקום (placeholder) שגורם אחר מחליף בערך האמיתי בגבול הרשת.
אין כאן סיפור על מודל שהופך לעוין. המנגנון בנאלי הרבה יותר. סוכן קורא דף אינטרנט, קובץ README או תגובה ב-issue המכילים הוראות, והוא מבצע אותן – כיוון שמבחינת מודל שפה אין הבדל בין טקסט שכתבת לבין טקסט שהוא משך מהרשת. זוהי הזרקת פרומפט (prompt injection). ברגע שזה קורה, הנזק מוגבל אך ורק לדבר אחד: מה שהתהליך מסוגל לקרוא. אם טרם הגדרת גבולות, המדריך הרצת סוכן תכנות בצורה מאובטחת על שרת מכסה את שלבי הבידוד שעליהם מתבסס מדריך זה.
מודל האיומים בשפה פשוטה
הריצו פקודה זו כמשתמש שבו פועל הסוכן שלכם.
tr '\0' '\n' < /proc/self/environ | grep -iE 'key|token|secret|password'כל שורה שהיא מדפיסה נמצאת במרחק בקשת HTTP אחת משרת של גורם זר. כעת, בדקו מה נמצא על הדיסק בקרבת הסוכן.
grep -rIl --exclude-dir=.git -e 'API_KEY' -e 'SECRET' ~/projects
find ~ -maxdepth 3 -name '.env' -o -name 'credentials' -o -name '*.pem'סוכן בעל גישה ל-shell אינו זקוק לניצול מתוחכם כדי להוציא את הנתונים הללו. ארבע נתיבים רגילים עושים את העבודה, וכולם נראים כמו פעילות תקינה בלוגים:
- פקודת
curlאוfetchיוצאת לכל מארח, כאשר הערך מוצמד למחרוזת השאילתה (query string). - פקודות
git commitו-git pushלמאגר (repository) שהסוכן מורשה לכתוב אליו. - סקריפט התקנת חבילה, המריץ קוד שרירותי תחת המשתמש של הסוכן.
- שאילתת DNS לשם מארח המכיל את הערך, מה שמאפשר הוצאת מידע גם כאשר תעבורת HTTP יוצאת חסומה.
אי אפשר לפתור בעיה זו באמצעות סקירה ידנית בלבד. הפתרון הוא לוודא שאין שום דבר בעל ערך בהישג ידו של הסוכן.
סוד שנמצא בעץ העבודה הוא סוד שנמצא בחלון ההקשר
סוכן קורא קבצים. קובץ .env במאגר שבו הוא עובד ייקרא, וברגע שנקרא הוא נמצא בחלון ההקשר. המשמעות היא שהוא מופיע בתמליל, בכל לוג שאתם שומרים, ובכל מה שהסוכן יכתוב בהמשך.
לפני, כאשר המפתח נמצא בעץ שבו הסוכן עובד:
cd ~/projects/billing
cat .env
# STRIPE_SECRET_KEY=sk_live_...
# DATABASE_URL=postgres://app:hunter2@db.internal:5432/billingאחרי, כאשר הקובץ הועבר למקום שאינו נגיש:
sudo install -d -m 750 -o root -g agent-review /etc/agent-review
sudo install -m 640 -o root -g agent-review ~/projects/billing/.env /etc/agent-review/billing.env
rm ~/projects/billing/.envהמשתמש של הסוכן כבר לא יכול לפתוח את הקובץ, כיוון שעץ העבודה כבר לא מכיל אותו. חוקי מניעה (Deny rules) בהגדרות של הסוכן עצמו הם שכבת הגנה שנייה, לא הראשונה. Claude Code קורא חוקי הרשאות מתוך .claude/settings.json בפרויקט:
{
"permissions": {
"deny": ["Read(./.env)", "Read(./secrets/**)", "Read(./**/*.pem)"]
}
}זה מונע טעות בתום לב שבה הסוכן פותח קובץ תוך כדי סריקה. זה לא מונע מהוראה שהוזרקה להריץ base64 .env, כיוון שזו פקודת shell ולא קריאת קובץ. השאלה אם תתבקשו לאשר לפני הרצת פקודה כזו תלויה במצב ההרשאות של הסשן, ו־מצב auto יהפוך לברירת המחדל ב-Claude Code באוגוסט 2026, כך ששרת שאינכם מנטרים יריץ יותר פקודות כאלו ללא בקשת אישור. אותה מגבלה חלה על כל דבר שמעצב את הרגלי הסוכן במקום את ההרשאות שלו: מיומנות שמחייבת את הסוכן לבצע את השינוי המינימלי שעובד מונעת מהרצה לגלוש לקבצים שלא היה לו עניין לפתוח, אך זו עדיין עצה שהמודל יכול להשתכנע להתעלם ממנה. התייחסו לקובץ ההגדרות כאל מעקה בטיחות ולהרשאות מערכת הקבצים כאל החומה. אותה הפרדה חלה בתוך מכולות: קובצי env וסודות ב-Docker Compose מכסים את הגרסה של בעיה זו שכבה אחת למטה.
הקצו לכל סוכן משתמש ללא הרשאות מיוחדות
אם הסוכן פועל תחת המשתמש שלכם, הוא יורש את מפתחות ה-SSH שלכם, את פרטי הגישה לענן ואת היסטוריית ה-shell שלכם. יצירת משתמש נפרד דורשת פקודה אחת בלבד ומבטלת את כל הסיכונים הללו.
sudo adduser --disabled-password --gecos "" agent-review
sudo chmod 700 /home/agent-review
sudo -u agent-review cat ~/.ssh/id_ed25519השורה האחרונה חייבת להיכשל עם cat: /home/you/.ssh/id_ed25519: Permission denied. אם היא מציגה מפתח, סימן שתיקיית הבית שלכם ניתנת לקריאה על ידי הקבוצה או על ידי כלל המשתמשים, ופקודת chmod 700 ~ תתקן זאת. אל תוסיפו את משתמש הסוכן ל-sudo, ואל תעניקו לו חוק NOPASSWD רחב יותר מהפקודה הבודדת שהוא באמת צריך. המדריך משתמשים בעלי הרשאות מינימליות ב-VPS מפרט את נושאי הקבוצות וה-sudoers. זכרו את ההפרדה הזו כאשר אתם מריצים יותר מסשן אחד על השרת, כיוון ש-סשן Claude Code אחד יכול לשלוח טקסט ישירות לאחר, וכל מה שהסשן הראשון מחזיק עלול לעבור בערוץ זה בהודעה אחת.
כדאי להוסיף גבול נוסף ב-VPS בענן. שירות המטא-דאטה של המופע מגיב בכתובת link-local קבועה, ולעיתים קרובות הוא מספק פרטי גישה (credentials) לכל מי שמבקש אותם.
sudo iptables -A OUTPUT -m owner --uid-owner agent-review -d 169.254.169.254 -j REJECTבדקו זאת מצד הסוכן. הפקודה sudo -u agent-review curl -s --max-time 3 http://169.254.169.254/ צריכה לא להדפיס דבר ולצאת עם קוד שגיאה, כיוון שהחבילה נדחית לפני שהיא עוזבת את השרת.
הזרקת פרטי הזדהות בנקודת הקצה
התבנית שפותרת זאת בפועל היא הזרקת פרטי הזדהות (credential injection). הסוכן לעולם אינו מחזיק מפתח אמיתי. הוא שולח את הבקשה שלו דרך gateway מקומי, וה-gateway מחליף מציין מיקום (placeholder) בסוד האמיתי בדרך החוצה. הסוד נשמר באחסון של ה-gateway, בתהליך נפרד, בבעלות משתמש אחר.
OneCLI הוא מימוש קוד פתוח אחד לכך, תחת רישיון Apache-2.0, והוא רץ כמכולה לצד הסוכן. נכון ליולי 2026, הפרויקט מתעד את התצורה הזו:
git clone https://github.com/onecli/onecli.git
cd onecli
docker compose -f docker/docker-compose.yml up -d --waitלוח הבקרה מאזין בפורט 10254 וה-gateway בפורט 10255. אתם שומרים את פרט ההזדהות האמיתי פעם אחת, ולאחר מכן נותנים לכל סוכן ערך מציין מיקום במקום המפתח, בצירוף אסימון גישה (access token) מוגבל משלו, אותו הוא שולח בתוך ה-header מסוג Proxy-Authorization. ה-gateway מתאים את הבקשה היוצאת לפי ה-host וה-path, מפענח את פרט ההזדהות התואם ומבצע את ההחלפה. סביבת הסוכן אינה מכילה דבר ששווה לגנוב.
הערך כאן אינו ההצפנה. הערך הוא שהשאלה "במה השתמש הסוכן הזה, ומתי" הופכת לשאילתת לוגים. אתם קוראים נתיב ביקורת (audit trail) אחד במקום לנחש איזו מתוך שש סביבות החזיקה עותק של המפתח.
העבירו את ה-secret לתהליך, לא לסביבת העבודה
אם אתם מריצים את ה-agent תחת systemd, אין לכם צורך במשתני סביבה כלל. LoadCredential= מציב את ה-secret בתיקייה פרטית שרק השירות הספציפי יכול לקרוא, והיא נחשפת כ-%d בקובץ ה-unit וכ-$CREDENTIALS_DIRECTORY בתוך התהליך. הערך לעולם אינו מופיע ב-/proc/<pid>/environ, לכן ps eww לא יכול להציג אותו, והתיקייה נמחקת כאשר השירות נעצר.
הצפינו את פרטי ההזדהות עבור המכונה תחילה. הפקודות הבאות לקוחות מתיעוד ה-systemd ועובדות על systemd 250 ומעלה, מה שמכסה את Ubuntu 24.04 ואת Debian 13:
echo -n 'sk-example-value' > /tmp/plain.txt
sudo systemd-creds encrypt --name=api_key /tmp/plain.txt /etc/credstore/api_key.cred
shred -u /tmp/plain.txt
sudo systemd-run -P --wait -p LoadCredentialEncrypted=api_key:/etc/credstore/api_key.cred \
systemd-creds cat api_keyהפקודה האחרונה מדפיסה את sk-example-value. זה מוכיח שהקובץ המוצפן מפוענח בהצלחה במארח זה. לאחר מכן, הפנו אליו מתוך ה-unit:
[Service]
User=agent-review
LoadCredentialEncrypted=api_key:/etc/credstore/api_key.cred
Environment=AGENT_KEY_FILE=%d/api_key
ExecStart=/usr/local/bin/agent-workerקוד ה-agent שלכם פותח את הקובץ ב-$AGENT_KEY_FILE כאשר הוא זקוק לערך. קריאת קובץ היא פעולה רגעית. משתנה סביבה נשאר פעיל לאורך כל חיי התהליך, בכל תהליך בן שהוא יוצר.
העדיפו אסימונים בעלי תוקף קצר על פני מפתחות בעלי תוקף ארוך
מפתח שלעולם אינו פג תוקף נותר תקף בכל פעם שהוא מופיע, גם חודשים לאחר מכן, בתוך לוג או תמלול. במקום שבו השירות מציע אסימון הפעלה (session token), השתמשו באסימון זה והגדירו לו את משך החיים הקצר ביותר שהמשימה מאפשרת.
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/agent-readonly \
--role-session-name agent-review \
--duration-seconds 900חמש-עשרה דקות הן פרק הזמן המינימלי ש־AWS STS (שירות אסימוני אבטחה) מקבל, וזה בדרך כלל מספיק למשימת סוכן אחת. עבור GitHub, העניקו למשתמש הסוכן חשבון gh משלו עם אסימון בעל הרשאות מוגדרות היטב (fine-grained), המוגבל למאגר (repository) היחיד שבו הוא פועל, כך ש־gh auth token בתוך אותה הפעלה יחזיר משהו שלא יכול לגשת לשום דבר אחר. הגבילו תחילה לפי משאב, ולאחר מכן לפי זמן.
אימות, ולאחר מכן אימות חוזר
כדאי להריץ שלוש בדיקות לאחר כל שינוי בהגדרות של סוכן (agent). יש להריץ אותן כמשתמש שמריץ את הסוכן, ולא כמשתמש המערכת שלכם.
sudo -u agent-review env | grep -iE 'key|token|secret'
sudo -u agent-review ls -la /home/you/ 2>&1 | head -3
sudo -u agent-review curl -s -o /dev/null -w '%{http_code}\n' --max-time 5 https://api.github.com/userהבדיקה הראשונה לא אמורה להדפיס דבר. הבדיקה השנייה אמורה להדפיס ls: cannot open directory '/home/you/': Permission denied. הבדיקה השלישית מציגה את הזהות שמציג נתיב הרשת של הסוכן; זו השאלה שדפוס ה-gateway נועד לענות עליה: 401 פירושו שהסוכן אינו נושא הרשאת GitHub משלו, ו-200 פירושו שהוא נושא הרשאה כזו, לכן עליכם לדעת באיזה token מדובר. אם אתם מריצים סוכנים ללא השגחה, שליטה בעלויות של סוכני AI על גבי VPS מכסה את מגבלות התקציב שמשתלבות עם מגבלות גישה אלו.
FAQ
האם אני יכול פשוט לסמוך על המודל שלא ידליף את המפתחות שלי?
לא, כיוון שהמודל אינו התוקף במודל האיומים הזה. הסוכן קורא טקסט מדפי אינטרנט, ממאגרים וממערכות לניהול משימות, וטקסט זה עלול להכיל הוראות. למודל אין דרך אמינה להבדיל בין ההוראות שלך לבין טקסט שהוא משך. כל מנגנון בקרה שתלוי בבחירה נכונה של המודל ייכשל ברגע שתופיע הוראה מוזרקת משכנעת; לכן, הבקרה חייבת להתבצע ברמת מערכת ההפעלה או הרשת.
האם משתני סביבה הם באמת כל כך גרועים עבור סודות של סוכנים?
הם גרועים בהיבט ספציפי אחד: הם עוברים בירושה. כל תהליך בן שהסוכן מפעיל מקבל עותק שלהם, כולל סקריפט בנייה, מריץ בדיקות, או כל hook של התקנת חבילות. המשתנים ניתנים לקריאה גם דרך /proc/<pid>/environ על ידי אותו משתמש, כך שכל דבר שהסוכן מריץ יכול לקרוא אותם מבלי שהסוכן יעביר אותם הלאה. קריאת קובץ ברגע השימוש, באמצעות LoadCredential= או gateway, מגבילה את החשיפה לאותו רגע בלבד.
האם אחסון סודות בכספת (vault) פותר את הבעיה כשלעצמו?
רק באופן חלקי. כספת פותרת את בעיית האחסון. אם אתה מארח את הכספת בעצמך, היא זקוקה לתהליך הקשחה משלה, כיוון ש-שרת Vaultwarden נפרץ בדרך כלל דרך אסימון הניהול או קובץ הגיבוי שלו, ולא דרך הפריטים המוצפנים שהוא מחזיק. זה לא פותר את השלב האחרון, שבו גורם כלשהו מושך את הסוד מהכספת ומעביר אותו לסוכן כמשתנה סביבה, מה שמחזיר אותך לנקודת ההתחלה. מה שקובע הוא מי מבצע את ההחלפה. אם הסוכן מושך את הסוד, הסוכן מחזיק בסוד. אם gateway או מערכת ה-init מבצעים את ההחלפה מחוץ לתהליך של הסוכן, הסוכן לעולם לא מחזיק בו.
איך אדע אם סוכן כבר הדליף משהו?
בדרך כלל לא ניתן לדעת זאת בדיעבד, וזו הטענה בעד שימוש ב-gateway. בלעדיו, הראיות שלך מפוזרות בהיסטוריית ה-shell, בתמלול של הסוכן, ובלוגים של חיבורים יוצאים שסביר להניח שאינך שומר. עם gateway לאימות הרשאות, כל שימוש בפרטי גישה מתועד בשורה אחת עם זהות הסוכן וחותמת זמן. אם אתה חושד בדליפה, בצע תחילה החלפת מפתח (rotation) ורק לאחר מכן חקור. החלפת מפתח היא פעולה זולה, בעוד שוודאות אינה כזו.
מהו המינימום שעלי לעשות היום?
העבר כל קובץ .env אל מחוץ לספריות שבהן הסוכנים שלך עובדים, וצור משתמש ללא הרשאות מיוחדות עבור כל סוכן. שני השינויים האלה לוקחים כעשר דקות וחוסמים את הנתיב הנפוץ ביותר, שבו סוכן קורא קובץ הרשאות שלא הייתה לו סיבה להימצא ליד הקוד. ה-gateway והאסימונים קצרי-הטווח הם השלב הבא, לא הראשון. אותה נקודת התחלה תקפה לכל סביבת הרצה של סוכנים, כולל הרצה בטוחה של סוכן אוטונומי על גבי VPS.