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)"]
}
}यह एजेंट द्वारा एक्सप्लोर करते समय गलती से फाइल खोलने की संभावना को रोकता है, और यह फाइल टूल्स से आगे तक काम करता है: Claude Code इन रूल्स को Bash में पहचाने जाने वाले फाइल कमांड्स जैसे cat और head, और < .env जैसे रीडायरेक्शन के टारगेट्स के खिलाफ भी चेक करता है। यह किसी इंजेक्ट किए गए इंस्ट्रक्शन को प्रोजेक्ट डायरेक्टरी के अंदर grep -r KEY . चलाने से, या किसी छोटी Python स्क्रिप्ट को जो खुद .env खोलती है, उसे नहीं रोकता है। ऐसा इसलिए है क्योंकि परमिशन डॉक्यूमेंटेशन कहता है कि deny रूल्स उन कमांड्स पर लागू नहीं होते जो फाइलों का नाम लिए बिना उन्हें पढ़ते हैं, या उन सब-प्रोसेसेस पर जो खुद फाइलें खोलते हैं। इनके लिए, डॉक्यूमेंटेशन Claude Code के सैंडबॉक्स की ओर इशारा करता है, जो ऑपरेटिंग सिस्टम स्तर पर ब्लॉक को लागू करता है। क्या आपसे ऐसे कमांड चलने से पहले पूछा जाएगा, यह सेशन के परमिशन मोड पर निर्भर करता है, और auto mode अब इंटरैक्टिव Claude Code सेशन के लिए इन-बिल्ट स्टार्टिंग मोड है, इसलिए जिस सर्वर को आप मॉनिटर नहीं कर रहे हैं, वह ऐसे और भी कमांड्स बिना पूछे चलाएगा। यही सीमा उन चीजों पर भी लागू होती है जो एजेंट की आदतों को आकार देती हैं, न कि उसकी परमिशन को: एक स्किल जो एजेंट को सबसे छोटे काम करने वाले बदलाव तक सीमित रखती है उसे उन फाइलों में भटकने से रोकती है जिन्हें खोलने का उसका कोई काम नहीं था, लेकिन यह अभी भी ऐसी सलाह है जिसे मॉडल को तर्क देकर बदला जा सकता है। कॉन्फिगरेशन को एक गार्डरेल (सुरक्षा घेरा) और फाइलसिस्टम परमिशन को दीवार मानें। यही विभाजन कंटेनर्स के अंदर भी लागू होता है: env files and secrets in Docker Compose इस समस्या के उस संस्करण को कवर करता है जो एक परत नीचे मौजूद है।
प्रत्येक 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 करती है, तो agent का user उस file तक पहुँच सकता है। किसी file को नाम से खोलने के लिए उसके ऊपर की प्रत्येक directory पर search (x) permission की आवश्यकता होती है, न कि read permission की। इसलिए, 711 mode वाली home directory, जिसे कोई और list नहीं कर सकता, वह भी key को access करने देती है यदि key file स्वयं readable है। chmod 700 ~ उस path को बंद कर देता है, और chmod 600 ~/.ssh/id_ed25519 key को ठीक कर देता है। 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 दे देती है। endpoint पर कुछ भी यह जाँच नहीं करता कि कौन सा local user call कर रहा है: EC2 पर, AWS यह document करता है कि IMDSv1 calls के लिए किसी token की आवश्यकता नहीं होती है, और उस service से एक user को दूर रखने के लिए इसकी अपनी सलाह उस user के ID पर आधारित एक local firewall rule है।
sudo iptables -I OUTPUT 1 -p tcp -m owner --uid-owner agent-review -d 169.254.169.254 -j REJECTrule को -A के साथ append करने के बजाय -I के साथ सबसे ऊपर डालें। अपनी default allow outgoing policy के साथ ufw चलाने वाले host पर, ufw की अंतिम OUTPUT chain हर नए TCP connection को स्वीकार कर लेती है, इसलिए एक appended rule तक कभी नहीं पहुँचा जाता है। iptables rules भी reboot पर गायब हो जाते हैं जब तक कि आप उन्हें persist न करें, और यदि आपने metadata service का IPv6 endpoint, [fd00:ec2::254], सक्षम किया है, तो ip6tables के साथ वही rule जोड़ें।
इसे agent की ओर से जाँचें। sudo -u agent-review curl -s --max-time 3 http://169.254.169.254/ को कुछ भी print नहीं करना चाहिए और non-zero exit देना चाहिए, क्योंकि packet के box से बाहर निकलने से पहले ही उसे reject कर दिया जाता है। यदि यह एक listing print करता है, तो rule तक नहीं पहुँचा जा रहा है।
बाउंड्री पर क्रेडेंशियल इंजेक्ट करें
इस समस्या का वास्तविक समाधान क्रेडेंशियल इंजेक्शन है। एजेंट के पास कभी भी वास्तविक की (key) नहीं होती है। यह अपना अनुरोध एक लोकल गेटवे के माध्यम से भेजता है, और गेटवे बाहर जाते समय वास्तविक सीक्रेट के लिए एक प्लेसहोल्डर को बदल देता है। सीक्रेट गेटवे के स्टोरेज में, एक अलग प्रोसेस में रहता है, जिसका मालिक एक अलग यूजर होता है।
OneCLI इसका एक ओपन सोर्स कार्यान्वयन है। यह इसके ee/ डायरेक्टरीज को छोड़कर Apache-2.0 लाइसेंस प्राप्त है, जो एक अलग एंटरप्राइज लाइसेंस के अंतर्गत आते हैं जिसके लिए प्रोडक्शन उपयोग हेतु सब्सक्रिप्शन की आवश्यकता होती है, और यह एजेंट के साथ कंटेनरों में चलता है। अक्टूबर 2026 तक, प्रोजेक्ट इस Docker Compose सेटअप का दस्तावेजीकरण करता है, जो compose फाइल के बगल में स्थित .env फाइल से तीन जनरेट किए गए सीक्रेट्स को पढ़ता है:
git clone https://github.com/onecli/onecli.git && cd onecli/docker
cat > .env <<EOF
SECRET_ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
GATEWAY_INTERNAL_SECRET=$(head -c 32 /dev/urandom | base64)
BETTER_AUTH_SECRET=$(head -c 32 /dev/urandom | base64)
COMPOSE_PROFILES=runner
EOF
chmod 600 .env
docker compose up -d --waitउस .env फाइल का बैकअप लें, क्योंकि SECRET_ENCRYPTION_KEY खोने से इसके साथ स्टोर किए गए सभी सीक्रेट्स अनाथ (orphaned) हो जाते हैं। डैशबोर्ड पोर्ट 10254 पर और गेटवे पोर्ट 10255 पर लिसन करता है, जो डिफ़ॉल्ट रूप से 127.0.0.1 पर बाउंड होते हैं। आप वास्तविक क्रेडेंशियल को एक बार स्टोर करते हैं, फिर प्रत्येक एजेंट को की (key) के स्थान पर एक प्लेसहोल्डर वैल्यू और उसका अपना स्कोप्ड एक्सेस टोकन देते हैं, जिसे वह Proxy-Authorization हेडर में भेजता है। गेटवे होस्ट और पाथ द्वारा आउटबाउंड अनुरोध का मिलान करता है, मैचिंग क्रेडेंशियल को डिक्रिप्ट करता है, और उसे प्रतिस्थापित (substitute) कर देता है। एजेंट के एनवायरनमेंट में चुराने लायक कुछ भी नहीं होता है।
यहाँ मूल्य एन्क्रिप्शन में नहीं है। यह इस तथ्य में है कि "इस एजेंट ने क्या उपयोग किया, और कब" वाला प्रश्न एक लॉग क्वेरी बन जाता है। आप छह एनवायरनमेंट में से किसमें की (key) की कॉपी थी, यह अनुमान लगाने के बजाय एक ऑडिट ट्रेल पढ़ते हैं।
सीक्रेट को प्रोसेस को सौंपें, एनवायरनमेंट को नहीं
यदि आप agent को systemd के अंतर्गत चलाते हैं, तो आपको environment variables की बिल्कुल भी आवश्यकता नहीं है। LoadCredential= सीक्रेट को एक private directory में रखता है जिसे केवल service का user ही पढ़ सकता है, जिसे unit file में %d के रूप में और प्रोसेस के अंदर $CREDENTIALS_DIRECTORY के रूप में expose किया जाता है। यह प्रतिबंध user के आधार पर है, न कि service के आधार पर, इसलिए प्रत्येक agent service को अपना अलग user दें: समान user के रूप में चलने वाली कोई भी अन्य प्रोसेस उस directory को खोल सकती है, जब तक कि unit filesystem namespacing का उपयोग न करे, जैसे कि PrivateMounts=yes, जो इसे अन्य services से छिपा देता है। यह मान कभी भी /proc/<pid>/environ में दिखाई नहीं देता है, इसलिए ps eww इसे नहीं दिखा सकता है, और service रुकने पर directory गायब हो जाती है।
क्रेडेंशियल को पहले मशीन के लिए 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 प्रिंट करती है। यह साबित करता है कि encrypted file इस host पर decrypt हो जाती है। फिर unit file से इसे 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 प्रोसेस के जीवनकाल तक बना रहता है, और उन सभी 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 के रूप में चलाएँ।
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 प्रिंट होना चाहिए, हालाँकि यह केवल यह दर्शाता है कि directory को list नहीं किया जा सकता है; पहले किया गया cat टेस्ट यह साबित करता है कि इसके अंदर की किसी file को पढ़ा नहीं जा सकता है। तीसरी जाँच आपको बताती है कि एजेंट का network path कौन सी पहचान प्रस्तुत करता है, जो कि वह प्रश्न है जिसका उत्तर देने के लिए gateway pattern मौजूद है: 401 का अर्थ है कि request बिना किसी ऐसे credential के GitHub तक पहुँची जिसे GitHub स्वीकार करता है (या तो कोई credential नहीं है, या जो है वह अमान्य या expired है), और 200 का अर्थ है कि यह एक मान्य credential ले जा रहा है, इसलिए आपको पता होना चाहिए कि यह कौन सा token है। यदि आप एजेंटों को unattended चलाते हैं, तो VPS पर AI एजेंट की लागत को नियंत्रित करना उन budget limits को कवर करता है जो इन access limits के साथ जोड़ी जाती हैं।
FAQ
क्या मैं केवल मॉडल पर भरोसा कर सकता हूँ कि वह मेरी keys लीक नहीं करेगा?
नहीं, क्योंकि इस threat model में मॉडल हमलावर नहीं है। एजेंट वेब पेजों, रिपॉजिटरी और इश्यू ट्रैकर्स से टेक्स्ट पढ़ता है, और उस टेक्स्ट में निर्देश हो सकते हैं। मॉडल के पास आपके निर्देशों और उसके द्वारा प्राप्त किए गए टेक्स्ट के बीच अंतर करने का कोई विश्वसनीय तरीका नहीं है। कोई भी नियंत्रण जो मॉडल के सही चुनाव करने पर निर्भर करता है, वह पहली बार में ही विफल हो जाता है जब कोई इंजेक्ट किया गया निर्देश विश्वसनीय लगता है। इसलिए, नियंत्रण को ऑपरेटिंग सिस्टम या नेटवर्क स्तर पर होना चाहिए।
क्या एजेंट सीक्रेट्स के लिए environment variables वास्तव में इतने बुरे हैं?
वे एक विशिष्ट तरीके से बुरे हैं: वे इनहेरिट (inherit) हो जाते हैं। एजेंट द्वारा शुरू की गई हर child process को उनकी एक कॉपी मिल जाती है, जिसमें बिल्ड स्क्रिप्ट, टेस्ट रनर और कोई भी पैकेज इंस्टॉल हुक शामिल है। ये वेरिएबल्स उसी यूजर द्वारा /proc/<pid>/environ के माध्यम से भी पढ़े जा सकते हैं, इसलिए एजेंट जो कुछ भी चलाता है, वह एजेंट द्वारा उन्हें पास किए बिना ही उन्हें पढ़ सकता है। उपयोग के समय पढ़ी गई फाइल, LoadCredential= या गेटवे के साथ, जोखिम को केवल उसी क्षण तक सीमित रखती है।
क्या सीक्रेट्स को वॉल्ट में रखने से यह समस्या अपने आप हल हो जाती है?
केवल आंशिक रूप से। वॉल्ट स्टोरेज को ठीक करता है। यदि आप उस वॉल्ट को खुद होस्ट करते हैं, तो उसे अपनी सुरक्षा को मजबूत करने की आवश्यकता होती है, क्योंकि एक Vaultwarden सर्वर आमतौर पर उसके एडमिन टोकन या बैकअप फाइल के माध्यम से हैक होता है, न कि उसके द्वारा रखे गए एन्क्रिप्टेड आइटम्स के माध्यम से। यह अंतिम चरण को ठीक नहीं करता है, जहाँ कोई चीज वॉल्ट से सीक्रेट निकालती है और उसे environment variable के रूप में एजेंट को दे देती है, जो आपको वापस वहीं ले आता है जहाँ से आपने शुरुआत की थी। मायने यह रखता है कि सब्स्टिट्यूशन (substitution) कौन करता है। यदि एजेंट सीक्रेट प्राप्त करता है, तो सीक्रेट एजेंट के पास होता है। यदि कोई गेटवे या init सिस्टम एजेंट की प्रोसेस के बाहर सब्स्टिट्यूशन करता है, तो एजेंट के पास कभी सीक्रेट नहीं होता।
मुझे कैसे पता चलेगा कि किसी एजेंट ने पहले ही कुछ लीक कर दिया है?
आमतौर पर आप बाद में यह नहीं बता सकते, और यही गेटवे के पक्ष में तर्क है। इसके बिना, आपके प्रमाण शेल हिस्ट्री, एजेंट के ट्रांसक्रिप्ट और आउटबाउंड कनेक्शन लॉग्स में बिखरे होते हैं, जिन्हें शायद आप रख भी नहीं रहे हैं। क्रेडेंशियल गेटवे के साथ, क्रेडेंशियल का हर उपयोग एजेंट की पहचान और टाइमस्टैम्प के साथ एक लाइन में दर्ज होता है। यदि आपको लीक का संदेह है, तो पहले की (key) रोटेट करें और बाद में जांच करें। रोटेशन सस्ता है और निश्चितता नहीं।
आज मुझे कम से कम क्या करना चाहिए?
हर .env फाइल को उन डायरेक्टरी से बाहर निकालें जिनमें आपके एजेंट काम करते हैं, और प्रति एजेंट एक unprivileged यूजर बनाएं। ये दो बदलाव लगभग दस मिनट लेते हैं और सबसे सामान्य रास्ते को बंद कर देते हैं, जो कि एक एजेंट का ऐसी क्रेडेंशियल फाइल को पढ़ना है जिसे कोड के पास होने का कोई कारण नहीं था। गेटवे और शॉर्ट-लाइव्ड टोकन अगला कदम हैं, पहला नहीं। यही शुरुआती बिंदु किसी भी एजेंट रनटाइम पर लागू होता है, जिसमें VPS पर सुरक्षित रूप से एक ऑटोनॉमस एजेंट चलाना शामिल है।