AI agents से API keys को सुरक्षित कैसे रखें
AI agents को सीधे API keys देने से वे leak हो सकती हैं। इस लेख में जानें कि कैसे short-lived tokens और credential gateway का उपयोग करके आप अपने secrets को सुरक्षित रख सकते हैं।
AI agents से secrets को सुरक्षित रखने का अर्थ
AI agent एक सामान्य Linux process है जो commands चलाती है। उस process के पास मौजूद हर environment variable को उसका code पढ़ सकता है, इसलिए agent के environment में मौजूद कोई भी API key वह key है जिसे agent किसी भी host को भेज सकता है जिसे वह access कर सकता है। Agent से secrets को दूर रखने का अर्थ है उसे key के बजाय एक handle देना: एक कम समय के लिए वैध (short-lived) scoped token, या एक ऐसा placeholder जिसे network boundary पर कोई अन्य माध्यम वास्तविक value से बदल दे।
यह किसी model के hostile होने की कहानी नहीं है। इसकी कार्यप्रणाली अधिक साधारण है। एक agent कोई web page, README, या issue comment पढ़ता है जिसमें निर्देश होते हैं, और वह उनका पालन करता है, क्योंकि language model के लिए आपके द्वारा लिखे गए text और उसके द्वारा fetch किए गए text में कोई अंतर नहीं होता। इसे prompt injection कहते हैं। एक बार जब यह हो जाता है, तो नुकसान की सीमा केवल एक ही चीज़ से तय होती है: वह process क्या पढ़ सकती है। यदि आपने अभी तक कोई boundary तय नहीं की है, तो server पर coding agent को सुरक्षित रूप से चलाना उन isolation चरणों को कवर करता है जिनके ऊपर यह guide आधारित है।
थ्रेट मॉडल को सरल शब्दों में समझना
इसे उस user के रूप में चलाएं जिसके तहत आपका agent चलता है।
tr '\0' '\n' < /proc/self/environ | grep -iE 'key|token|secret|password'यह जो भी लाइन print करता है, वह किसी अनजान व्यक्ति के सर्वर से एक HTTP request की दूरी पर है। अब देखें कि agent के पास disk पर क्या मौजूद है।
grep -rIl --exclude-dir=.git -e 'API_KEY' -e 'SECRET' ~/projects
find ~ -maxdepth 3 -name '.env' -o -name 'credentials' -o -name '*.pem'Shell वाले agent को उस डेटा को बाहर निकालने के लिए किसी चतुर exploit की आवश्यकता नहीं होती है। चार सामान्य paths यह काम कर देते हैं, और ये चारों log में सामान्य काम की तरह दिखते हैं:
- किसी भी host पर outbound
curlयाfetch, जिसमें value एक query string में हो। - एक ऐसी repository पर
git commitऔरgit push, जिसमें agent लिख सकता है। - एक package install script, जो agent के user के रूप में arbitrary code चलाती है।
- एक ऐसे hostname का DNS lookup जिसमें वह value हो, जो HTTP egress ब्लॉक होने पर भी काम करता है।
आप केवल review करके इससे नहीं बच सकते। इसका समाधान यह सुनिश्चित करना है कि पहुंच के भीतर कोई भी मूल्यवान चीज़ न हो।
वर्किंग ट्री में मौजूद secret, कॉन्टेक्स्ट विंडो में भी एक secret है
एक एजेंट फाइलों को पढ़ता है। जिस रिपॉजिटरी में वह काम कर रहा है, उसमें मौजूद .env फाइल को पढ़ लिया जाएगा। एक बार पढ़ लिए जाने के बाद, यह कॉन्टेक्स्ट विंडो में आ जाती है, जिसका अर्थ है कि यह ट्रांसक्रिप्ट में, आपके द्वारा रखे गए किसी भी लॉग में, और एजेंट द्वारा आगे लिखी जाने वाली किसी भी चीज में शामिल हो जाती है।
पहले, जब key उस ट्री में मौजूद थी जिसमें एजेंट काम करता है:
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 नियम दूसरी परत हैं, पहली नहीं। Claude Code प्रोजेक्ट में .claude/settings.json से अनुमति के नियम पढ़ता है:
{
"permissions": {
"deny": ["Read(./.env)", "Read(./secrets/**)", "Read(./**/*.pem)"]
}
}यह एजेंट द्वारा एक्सप्लोर करते समय अनजाने में फाइल खोलने की गलती को रोकता है। यह किसी इंजेक्ट किए गए निर्देश को base64 .env चलाने से नहीं रोकता, क्योंकि यह एक शेल कमांड है, न कि फाइल रीड। उस कमांड के चलने से पहले आपसे पूछा जाएगा या नहीं, यह सेशन के परमिशन मोड पर निर्भर करता है, और अगस्त 2026 में auto mode, Claude Code का डिफ़ॉल्ट बन जाएगा, इसलिए जिस सर्वर पर आप नजर नहीं रख रहे हैं, वह बिना किसी संकेत के इनमें से अधिक कमांड चलाएगा। यही सीमा उन चीजों पर भी लागू होती है जो एजेंट की आदतों को आकार देती हैं, न कि उसकी अनुमतियों को: एक स्किल जो एजेंट को सबसे छोटे कार्यशील बदलाव तक सीमित रखती है उसे उन फाइलों में जाने से रोकती है जिन्हें खोलने का उसका कोई काम नहीं था, लेकिन यह अभी भी ऐसी सलाह है जिसे मॉडल को तर्क देकर बदला जा सकता है। कॉन्फ़िगरेशन को एक गार्डरेल (सुरक्षा घेरा) और फाइलसिस्टम की अनुमति को दीवार मानें। यही विभाजन कंटेनरों के अंदर भी लागू होता है: Docker Compose में env फाइलें और secrets इस समस्या के एक स्तर नीचे के संस्करण को कवर करता है।
प्रत्येक agent को अपना unprivileged user दें
यदि agent आपके user के रूप में चलता है, तो वह आपके SSH keys, cloud credentials और shell history को inherit कर लेता है। एक अलग user बनाने में केवल एक command का खर्च आता है और यह इन सभी जोखिमों को समाप्त कर देता है।
sudo adduser --disabled-password --gecos "" agent-review
sudo chmod 700 /home/agent-review
sudo -u agent-review cat ~/.ssh/id_ed25519अंतिम line को cat: /home/you/.ssh/id_ed25519: Permission denied के साथ fail होना चाहिए। यदि यह कोई key print करती है, तो आपकी home directory group या world readable है, और chmod 700 ~ इसे ठीक कर देगा। agent user को sudo में न जोड़ें, और उसे उस एक command से अधिक की NOPASSWD rule न दें जिसकी उसे वास्तव में आवश्यकता है। VPS पर Least privilege users में group और sudoers का विवरण दिया गया है। जब आप box पर एक से अधिक session चलाएं, तो इस अलगाव को ध्यान में रखें, क्योंकि एक Claude Code session सीधे दूसरे को text भेज सकता है, और जो कुछ भी पहले session के पास है, वह एक ही message में उस channel को पार कर सकता है।
cloud VPS पर एक और boundary जोड़ना उचित है। instance metadata service एक fixed link local address पर उत्तर देती है, और यह अक्सर पूछने वाले किसी भी चीज़ को role credentials दे देती है।
sudo iptables -A OUTPUT -m owner --uid-owner agent-review -d 169.254.169.254 -j REJECTइसे agent की ओर से जाँचे। sudo -u agent-review curl -s --max-time 3 http://169.254.169.254/ को कुछ भी print नहीं करना चाहिए और non-zero exit देना चाहिए, क्योंकि packet के box से बाहर निकलने से पहले ही उसे reject कर दिया जाता है।
बाउंड्री पर क्रेडेंशियल इंजेक्ट करें
इस समस्या का वास्तविक समाधान क्रेडेंशियल इंजेक्शन है। एजेंट के पास कभी भी वास्तविक की (key) नहीं होती है। यह अपना अनुरोध एक लोकल गेटवे के माध्यम से भेजता है, और गेटवे बाहर जाते समय वास्तविक सीक्रेट के लिए एक प्लेसहोल्डर को स्वैप कर देता है। सीक्रेट गेटवे के स्टोरेज में, एक अलग प्रोसेस में, और एक अलग यूजर के स्वामित्व में रहता है।
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 पर और गेटवे 10255 पर लिसन करता है। आप वास्तविक क्रेडेंशियल को एक बार स्टोर करते हैं, फिर प्रत्येक एजेंट को की (key) के स्थान पर एक प्लेसहोल्डर वैल्यू और उसका अपना स्कोप्ड एक्सेस टोकन देते हैं, जिसे वह Proxy-Authorization हेडर में भेजता है। गेटवे आउटबाउंड अनुरोध को होस्ट और पाथ के आधार पर मैच करता है, मैचिंग क्रेडेंशियल को डिक्रिप्ट करता है, और उसे प्रतिस्थापित कर देता है। एजेंट के एनवायरनमेंट में चुराने लायक कुछ भी नहीं होता है।
यहाँ मूल्य एन्क्रिप्शन में नहीं है। इसका मूल्य यह है कि "इस एजेंट ने क्या उपयोग किया, और कब" वाला प्रश्न एक लॉग क्वेरी बन जाता है। आप छह अलग-अलग एनवायरनमेंट में से किसमें की (key) की कॉपी थी, यह अनुमान लगाने के बजाय एक ऑडिट ट्रेल पढ़ते हैं।
Secret को process को सौंपें, environment को नहीं
यदि आप agent को systemd के अंतर्गत चलाते हैं, तो आपको environment variables की बिल्कुल भी आवश्यकता नहीं है। LoadCredential= secret को एक private directory में रखता है जिसे केवल वही service पढ़ सकती है, जिसे unit file में %d के रूप में और process के अंदर $CREDENTIALS_DIRECTORY के रूप में expose किया जाता है। यह मान कभी भी /proc/<pid>/environ में दिखाई नहीं देता है, इसलिए ps eww इसे नहीं दिखा सकता है, और service के रुकने पर directory गायब हो जाती है।
Credential को पहले machine के लिए encrypt करें। ये commands systemd documentation से ली गई हैं और 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अंतिम command sk-example-value को print करती है। यह सिद्ध करता है कि encrypted file इस host पर decrypt हो जाती है। फिर इसे unit से reference करें:
[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 code $AGENT_KEY_FILE पर file को तब खोलता है जब उसे मान की आवश्यकता होती है। file read एक क्षणिक प्रक्रिया है। एक environment variable process के पूरे जीवनकाल तक रहता है, और उन सभी child processes में भी जिन्हें वह spawn करता है।
लंबे समय तक चलने वाली कुंजियों के बजाय कम समय के लिए मान्य टोकन का उपयोग करें
एक ऐसी कुंजी जो कभी समाप्त नहीं होती, वह महीनों बाद भी लॉग या ट्रांसक्रिप्ट में मिलने पर मान्य रहती है। जहाँ सेवा एक सेशन टोकन प्रदान करती है, वहाँ सेशन टोकन का उपयोग करें और कार्य के अनुसार सबसे कम अवधि निर्धारित करें।
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/agent-readonly \
--role-session-name agent-review \
--duration-seconds 900AWS STS (security token service) द्वारा स्वीकार की जाने वाली न्यूनतम अवधि 15 मिनट है, और यह आमतौर पर एक एजेंट कार्य के लिए पर्याप्त होती है। GitHub के लिए, एजेंट उपयोगकर्ता को अपना स्वयं का gh लॉगिन दें, जिसमें उस विशिष्ट रिपॉजिटरी तक सीमित फाइन-ग्रेन्ड टोकन हो जिस पर वह काम करता है। इससे उस सेशन के भीतर gh auth token का उपयोग करने पर ऐसी एक्सेस मिलती है जो किसी अन्य चीज़ को प्रभावित नहीं कर सकती। पहले संसाधन के आधार पर दायरा (scope) तय करें, फिर समय के आधार पर।
सत्यापन करें, फिर बार-बार सत्यापन करें
एजेंट के सेटअप में किसी भी बदलाव के बाद तीन जाँचें करना उचित रहता है। इन्हें अपने user के बजाय एजेंट के user के रूप में चलाएँ।
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 प्रिंट होना चाहिए। तीसरी जाँच आपको बताती है कि एजेंट का network path कौन सी पहचान प्रस्तुत करता है, जो कि वह प्रश्न है जिसका उत्तर देने के लिए gateway pattern मौजूद है: 401 का अर्थ है कि एजेंट के पास अपना कोई GitHub credential नहीं है, और 200 का अर्थ है कि उसके पास एक credential है, इसलिए आपको पता होना चाहिए कि वह कौन सा token है। यदि आप एजेंट को बिना निगरानी के चलाते हैं, तो VPS पर AI एजेंट की लागत को नियंत्रित करना उन बजट सीमाओं को कवर करता है जो इन access सीमाओं के साथ जोड़ी जाती हैं।
FAQ
क्या मैं केवल मॉडल पर भरोसा कर सकता हूँ कि वह मेरी keys लीक नहीं करेगा?
नहीं, क्योंकि इस threat model में मॉडल हमलावर नहीं है। एजेंट वेब पेजों, रिपॉजिटरी और इश्यू ट्रैकर्स से टेक्स्ट पढ़ता है, और उस टेक्स्ट में निर्देश हो सकते हैं। मॉडल के पास आपके निर्देशों और उसके द्वारा प्राप्त किए गए टेक्स्ट के बीच अंतर करने का कोई विश्वसनीय तरीका नहीं है। कोई भी नियंत्रण जो मॉडल के सही चुनाव करने पर निर्भर करता है, वह पहली बार में ही विफल हो जाता है जब कोई इंजेक्ट किया गया निर्देश विश्वसनीय लगता है। इसलिए, नियंत्रण को ऑपरेटिंग सिस्टम या नेटवर्क स्तर पर होना चाहिए।
क्या एजेंट सीक्रेट्स के लिए environment variables वास्तव में इतने बुरे हैं?
वे एक विशिष्ट तरीके से बुरे हैं: वे इनहेरिट (inherit) हो जाते हैं। एजेंट द्वारा शुरू की गई हर child process को उनकी एक कॉपी मिल जाती है, जिसमें बिल्ड स्क्रिप्ट, टेस्ट रनर और कोई भी पैकेज इंस्टॉल हुक शामिल है। ये वेरिएबल्स उसी यूजर द्वारा /proc/<pid>/environ के माध्यम से भी पढ़े जा सकते हैं, इसलिए एजेंट जो कुछ भी चलाता है, वह एजेंट द्वारा उन्हें पास किए बिना ही उन्हें पढ़ सकता है। उपयोग के समय पढ़ी गई फाइल, LoadCredential= या गेटवे के साथ, जोखिम को केवल उसी क्षण तक सीमित रखती है।
क्या सीक्रेट्स को वॉल्ट में रखने से यह समस्या अपने आप हल हो जाती है?
केवल आंशिक रूप से। वॉल्ट स्टोरेज को ठीक करता है। यदि आप उस वॉल्ट को खुद होस्ट करते हैं, तो उसे अपनी सुरक्षा को मजबूत करने की आवश्यकता होती है, क्योंकि एक Vaultwarden सर्वर आमतौर पर उसके एडमिन टोकन या बैकअप फाइल के माध्यम से हैक होता है, न कि उसके द्वारा रखे गए एन्क्रिप्टेड आइटम्स के माध्यम से। यह अंतिम चरण को ठीक नहीं करता है, जहाँ कोई चीज वॉल्ट से सीक्रेट निकालती है और उसे environment variable के रूप में एजेंट को दे देती है, जो आपको वापस वहीं ले आता है जहाँ से आपने शुरुआत की थी। मायने यह रखता है कि सब्स्टिट्यूशन (substitution) कौन करता है। यदि एजेंट सीक्रेट प्राप्त करता है, तो सीक्रेट एजेंट के पास होता है। यदि कोई गेटवे या init सिस्टम एजेंट की प्रोसेस के बाहर सब्स्टिट्यूशन करता है, तो एजेंट के पास कभी सीक्रेट नहीं होता।
मुझे कैसे पता चलेगा कि किसी एजेंट ने पहले ही कुछ लीक कर दिया है?
आमतौर पर आप बाद में यह नहीं बता सकते, और यही गेटवे के पक्ष में तर्क है। इसके बिना, आपके प्रमाण शेल हिस्ट्री, एजेंट के ट्रांसक्रिप्ट और आउटबाउंड कनेक्शन लॉग्स में बिखरे होते हैं, जिन्हें शायद आप रख भी नहीं रहे हैं। क्रेडेंशियल गेटवे के साथ, क्रेडेंशियल का हर उपयोग एजेंट की पहचान और टाइमस्टैम्प के साथ एक लाइन में दर्ज होता है। यदि आपको लीक का संदेह है, तो पहले की (key) रोटेट करें और बाद में जांच करें। रोटेशन सस्ता है और निश्चितता नहीं।
आज मुझे कम से कम क्या करना चाहिए?
हर .env फाइल को उन डायरेक्टरी से बाहर निकालें जिनमें आपके एजेंट काम करते हैं, और प्रति एजेंट एक unprivileged यूजर बनाएं। ये दो बदलाव लगभग दस मिनट लेते हैं और सबसे सामान्य रास्ते को बंद कर देते हैं, जो कि एक एजेंट का ऐसी क्रेडेंशियल फाइल को पढ़ना है जिसे कोड के पास होने का कोई कारण नहीं था। गेटवे और शॉर्ट-लाइव्ड टोकन अगला कदम हैं, पहला नहीं। यही शुरुआती बिंदु किसी भी एजेंट रनटाइम पर लागू होता है, जिसमें VPS पर सुरक्षित रूप से एक ऑटोनॉमस एजेंट चलाना शामिल है।