SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor

Cara Lindungi Kunci API Daripada Ejen AI

Ejen AI boleh membocorkan kunci API melalui suntikan prompt. Elakkan risiko ini dengan menggunakan token jangka pendek dan gerbang kelayakan dan bukannya kunci akses utama.

Maksud menyimpan rahsia di luar ejen AI

Ejen AI ialah proses Linux biasa yang menjalankan perintah. Setiap pemboleh ubah persekitaran yang dipegang oleh proses itu boleh dibaca oleh kod yang dijalankannya. Oleh itu, kunci API dalam persekitaran ejen ialah kunci yang boleh dihantar oleh ejen ke mana-mana hos yang boleh dicapainya. Menyimpan rahsia di luar ejen bermaksud memberinya pemegang dan bukannya kunci: token berjangka pendek dengan skop terhad, atau ruang letak yang digantikan dengan nilai sebenar oleh komponen lain di sempadan rangkaian.

Ini bukan cerita tentang model yang menjadi berniat jahat. Mekanismenya lebih biasa. Ejen membaca halaman web, README atau komen isu yang mengandungi arahan, lalu mengikutinya kerana bagi model bahasa, tiada perbezaan antara teks yang anda tulis dengan teks yang diambilnya. Itulah prompt injection. Apabila perkara ini berlaku, skop kerosakan ditentukan tepat oleh satu perkara: perkara yang boleh dibaca oleh proses itu. Jika anda belum menetapkan sempadan, menjalankan ejen pengekodan dengan selamat pada pelayan menerangkan tahap pengasingan yang menjadi asas panduan ini.

Model ancaman dalam istilah mudah

Jalankan ini sebagai pengguna yang digunakan oleh ejen anda.

tr '\0' '\n' < /proc/self/environ | grep -iE 'key|token|secret|password'

Setiap baris yang dicetaknya ialah satu permintaan HTTP sahaja daripada pelayan orang yang tidak dikenali. Sekarang lihat perkara yang terdapat pada cakera berdekatan ejen itu.

grep -rIl --exclude-dir=.git -e 'API_KEY' -e 'SECRET' ~/projects
find ~ -maxdepth 3 -name '.env' -o -name 'credentials' -o -name '*.pem'

Ejen yang mempunyai shell tidak memerlukan eksploit yang canggih untuk mengeluarkan data tersebut. Empat laluan biasa sudah mencukupi, dan keempat-empatnya kelihatan seperti kerja biasa dalam log:

  • curl atau fetch keluar ke mana-mana hos, dengan nilai dalam rentetan pertanyaan.
  • git commit dan git push ke repositori yang boleh ditulis oleh ejen.
  • Skrip pemasangan pakej yang menjalankan kod sewenang-wenangnya sebagai pengguna ejen.
  • Carian DNS bagi nama hos yang mengandungi nilai tersebut. Nilai itu tetap boleh keluar walaupun egress HTTP disekat.

Anda tidak dapat menyelesaikan masalah ini hanya dengan membuat semakan. Penyelesaiannya ialah memastikan tiada data bernilai yang boleh dicapai.

Rahsia dalam working tree adalah rahsia dalam tetingkap konteks

Ejen membaca fail. Fail .env dalam repositori tempat ia bekerja akan dibaca, dan sebaik sahaja dibaca, ia berada dalam tetingkap konteks. Ini bermakna ia berada dalam transkrip, dalam sebarang log yang anda simpan, dan dalam apa jua yang ditulis oleh ejen seterusnya.

Sebelum ini, dengan kunci berada dalam tree tempat ejen bekerja:

cd ~/projects/billing
cat .env
# STRIPE_SECRET_KEY=sk_live_...
# DATABASE_URL=postgres://app:hunter2@db.internal:5432/billing

Selepas ini, dengan fail dialihkan ke luar jangkauan:

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

Pengguna ejen tidak lagi boleh membuka fail tersebut, kerana working tree tidak lagi mengandungi fail itu. Peraturan deny dalam konfigurasi ejen sendiri adalah lapisan kedua, bukan lapisan pertama. Claude Code membaca peraturan kebenaran daripada .claude/settings.json dalam projek:

{
  "permissions": {
    "deny": ["Read(./.env)", "Read(./secrets/**)", "Read(./**/*.pem)"]
  }
}

Ini menghalang kesilapan jujur ejen membuka fail semasa meneroka, dan ia menjangkaui alat fail: Claude Code juga menyemak peraturan ini terhadap arahan fail yang dikenalinya dalam Bash, seperti cat dan head, serta terhadap sasaran pengalihan (redirection) seperti < .env. Ia tidak menghalang arahan yang disuntik daripada menjalankan grep -r KEY . di dalam direktori projek, atau skrip Python pendek yang membuka .env sendiri, kerana dokumentasi kebenaran menyatakan peraturan deny tidak terpakai kepada arahan yang membaca fail tanpa menamakannya atau kepada subproses yang membuka fail dengan sendirinya. Untuk perkara tersebut, dokumentasi merujuk kepada sandbox Claude Code, yang menguatkuasakan sekatan pada peringkat sistem pengendalian. Sama ada anda ditanya sebelum arahan sedemikian dijalankan bergantung pada mod kebenaran sesi, dan auto mode kini merupakan mod permulaan terbina dalam untuk sesi interaktif Claude Code, jadi pelayan yang tidak anda pantau akan menjalankan lebih banyak arahan tersebut tanpa diminta. Had yang sama terpakai kepada apa sahaja yang membentuk tabiat ejen dan bukannya kebenarannya: kemahiran yang mengekalkan ejen pada perubahan terkecil yang berkesan menghalang sesuatu pelaksanaan daripada melencong ke dalam fail yang tidak sepatutnya dibuka, tetapi ia masih merupakan nasihat yang boleh dipujuk supaya diabaikan oleh model. Anggap konfigurasi sebagai penghadang dan kebenaran sistem fail sebagai dinding. Pembahagian yang sama terpakai di dalam kontena: env files dan rahsia dalam Docker Compose merangkumi versi masalah ini satu lapisan ke bawah.

Berikan setiap ejen pengguna tanpa keistimewaan (unprivileged) sendiri

Jika ejen berjalan sebagai anda, ia mewarisi kunci SSH, kelayakan awan dan sejarah shell anda. Pengguna berasingan hanya memerlukan satu arahan dan menghapuskan semua risiko tersebut.

sudo adduser --disabled-password --gecos "" agent-review
sudo chmod 700 /home/agent-review
sudo -u agent-review cat ~/.ssh/id_ed25519

Baris terakhir mesti gagal dengan cat: /home/you/.ssh/id_ed25519: Permission denied. Jika ia mencetak kunci, pengguna ejen boleh mencapai fail tersebut. Membuka fail mengikut nama memerlukan kebenaran gelintar (x) pada setiap direktori di atasnya, bukan kebenaran baca, jadi direktori home pada mod 711 yang tidak boleh disenaraikan oleh orang lain masih membenarkan kunci tersebut diakses jika fail kunci itu sendiri boleh dibaca. chmod 700 ~ menutup laluan tersebut, dan chmod 600 ~/.ssh/id_ed25519 membetulkan kunci itu. Jangan tambah pengguna ejen ke dalam sudo, dan jangan berikan peraturan NOPASSWD yang lebih luas daripada satu arahan yang benar-benar diperlukannya. Pengguna dengan keistimewaan minimum pada VPS membincangkan perincian kumpulan dan sudoers. Ingat pengasingan ini sebaik sahaja anda menjalankan lebih daripada satu sesi pada kotak tersebut, kerana satu sesi Claude Code boleh menghantar teks terus kepada yang lain, dan apa sahaja yang dipegang oleh sesi pertama boleh merentasi saluran itu dalam satu mesej.

Satu lagi sempadan yang perlu ditambah pada VPS awan adalah penting. Servis metadata instans menjawab pada alamat link-local yang tetap, dan ia sering memberikan kelayakan peranan kepada sesiapa sahaja yang memintanya. Tiada apa-apa pada endpoint tersebut yang menyemak pengguna tempatan mana yang memanggil: pada EC2, AWS mendokumenkan bahawa panggilan IMDSv1 tidak memerlukan token, dan nasihatnya sendiri untuk menghalang pengguna daripada servis tersebut adalah peraturan firewall tempatan yang dikunci pada ID pengguna tersebut.

sudo iptables -I OUTPUT 1 -p tcp -m owner --uid-owner agent-review -d 169.254.169.254 -j REJECT

Masukkan peraturan di bahagian atas dengan -I dan bukannya menambahnya di bahagian akhir dengan -A. Pada hos yang menjalankan ufw dengan polisi lalai membenarkan keluar (allow outgoing), rantaian OUTPUT terakhir ufw menerima setiap sambungan TCP baharu, jadi peraturan yang ditambah di akhir tidak akan dicapai. Peraturan iptables juga akan hilang semasa but semula melainkan anda mengekalkannya, dan jika anda telah mendayakan endpoint IPv6 servis metadata, [fd00:ec2::254], tambah peraturan yang sama dengan ip6tables.

Semak daripada sisi ejen. sudo -u agent-review curl -s --max-time 3 http://169.254.169.254/ sepatutnya tidak mencetak apa-apa dan keluar dengan status bukan sifar, kerana paket tersebut ditolak sebelum ia meninggalkan kotak. Jika ia mencetak senarai, peraturan tersebut tidak dicapai.

Suntikan kelayakan pada sempadan

Corak yang benar-benar menyelesaikan masalah ini ialah suntikan kelayakan. Ejen tidak pernah memegang kunci sebenar. Ia menghantar permintaannya melalui get laluan tempatan, dan get laluan tersebut menukar pemegang tempat dengan rahsia sebenar semasa permintaan keluar. Rahsia tersebut disimpan dalam storan get laluan, dalam proses yang berbeza, dan dimiliki oleh pengguna yang berbeza.

OneCLI ialah satu pelaksanaan sumber terbuka bagi kaedah ini. Ia dilesenkan di bawah Apache-2.0 selain daripada direktori ee/ miliknya, yang membawa lesen perusahaan berasingan yang memerlukan langganan untuk kegunaan pengeluaran, dan ia berjalan dalam kontena bersebelahan dengan ejen. Setakat Oktober 2026, projek ini mendokumentasikan tetapan Docker Compose ini, yang membaca tiga rahsia yang dijana daripada fail .env di sebelah fail compose tersebut:

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

Buat sandaran bagi fail .env itu, kerana kehilangan SECRET_ENCRYPTION_KEY akan menyebabkan setiap rahsia yang disimpan dengannya menjadi yatim. Papan pemuka mendengar pada port 10254 dan get laluan pada 10255, kedua-duanya terikat pada 127.0.0.1 secara lalai. Anda menyimpan kelayakan sebenar sekali sahaja, kemudian berikan setiap ejen nilai pemegang tempat sebagai ganti kunci tersebut berserta token akses skopnya sendiri, yang dihantar dalam pengepala Proxy-Authorization. Get laluan memadankan permintaan keluar mengikut hos dan path, menyahsulit kelayakan yang sepadan, dan menggantikannya. Persekitaran ejen tidak menyimpan apa-apa yang bernilai untuk dicuri.

Nilai di sini bukanlah penyulitan. Nilainya ialah soalan "apakah yang digunakan oleh ejen ini, dan bila" menjadi satu pertanyaan log. Anda membaca satu jejak audit dan bukannya meneka persekitaran mana antara enam persekitaran yang menyimpan salinan kunci tersebut.

Serahkan rahsia kepada proses, bukan kepada persekitaran

Jika anda menjalankan ejen di bawah systemd, anda tidak memerlukan pemboleh ubah persekitaran sama sekali. LoadCredential= meletakkan rahsia dalam direktori peribadi yang hanya boleh dibaca oleh pengguna servis tersebut, didedahkan sebagai %d dalam fail unit dan sebagai $CREDENTIALS_DIRECTORY di dalam proses. Sekatan tersebut adalah mengikut pengguna, bukan mengikut servis, jadi berikan setiap servis ejen penggunanya sendiri: mana-mana proses lain yang berjalan sebagai pengguna yang sama boleh membuka direktori tersebut melainkan unit itu juga menggunakan filesystem namespacing, seperti PrivateMounts=yes, yang menyembunyikannya daripada servis lain. Nilai tersebut tidak pernah muncul dalam /proc/<pid>/environ, jadi ps eww tidak boleh memaparkannya, dan direktori tersebut hilang apabila servis berhenti.

Suluhkan kelayakan ke mesin terlebih dahulu. Perintah-perintah ini datang daripada dokumentasi systemd dan berfungsi pada systemd 250 atau lebih baharu, yang merangkumi Ubuntu 24.04 dan 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

Perintah terakhir mencetak sk-example-value. Ini membuktikan fail yang disulitkan itu dinyahsulit pada hos ini. Kemudian rujuk fail tersebut daripada 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

Kod ejen anda membuka fail di $AGENT_KEY_FILE apabila ia memerlukan nilai tersebut. Bacaan fail adalah seketika. Pemboleh ubah persekitaran kekal sepanjang hayat proses, dalam setiap anak proses yang dihasilkannya.

Utamakan token berjangka pendek berbanding kunci berjangka panjang

Kunci yang tidak pernah luput tempoh masih sah apabila muncul semula, beberapa bulan kemudian, dalam log atau transkrip. Jika servis menyediakan token sesi, gunakan token sesi itu dan tetapkan tempoh sah yang paling singkat yang masih mencukupi untuk tugas tersebut.

aws sts assume-role \
  --role-arn arn:aws:iam::123456789012:role/agent-readonly \
  --role-session-name agent-review \
  --duration-seconds 900

Lima belas minit ialah tempoh minimum yang diterima oleh AWS STS (security token service), dan biasanya tempoh itu mencukupi untuk satu tugas ejen. Untuk GitHub, berikan pengguna ejen gh log masuk tersendiri dengan token berbutiran halus yang dihadkan kepada satu repositori yang digunakan, supaya gh auth token dalam sesi itu hanya mengembalikan sesuatu yang tidak boleh mengakses apa-apa yang lain. Hadkan skop mengikut sumber terlebih dahulu, kemudian mengikut masa.

Sahkan, kemudian teruskan pengesahan

Tiga pemeriksaan perlu dijalankan selepas sebarang perubahan pada tetapan ejen. Jalankan pemeriksaan ini sebagai pengguna ejen, bukan sebagai diri anda sendiri.

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

Pemeriksaan pertama tidak sepatutnya memaparkan apa-apa. Pemeriksaan kedua sepatutnya memaparkan ls: cannot open directory '/home/you/': Permission denied, walaupun ia hanya menunjukkan direktori tersebut tidak boleh disenaraikan; ujian cat sebelum ini adalah bukti bahawa fail di dalamnya tidak boleh dibaca. Pemeriksaan ketiga memberitahu anda identiti yang dibentangkan oleh laluan rangkaian ejen, iaitu persoalan yang dijawab oleh corak gateway: 401 bermaksud permintaan sampai ke GitHub tanpa kelayakan yang diterima oleh GitHub, sama ada tiada langsung atau kelayakan tersebut tidak sah atau telah tamat tempoh, manakala 200 bermaksud ia membawa kelayakan yang sah, jadi anda harus tahu token yang mana satu digunakan. Jika anda menjalankan ejen tanpa pengawasan, mengawal kos ejen AI pada VPS merangkumi had bajet yang dipadankan dengan had akses ini.

FAQ

Bolehkah saya mempercayai model supaya tidak membocorkan kunci saya?

Tidak, kerana model bukan penyerang dalam model ancaman ini. Ejen membaca teks daripada halaman web, repositori dan penjejak isu, dan teks itu boleh mengandungi arahan. Model tidak mempunyai cara yang boleh dipercayai untuk membezakan arahan anda daripada teks yang diperolehnya. Sebarang kawalan yang bergantung pada model untuk membuat pilihan yang betul akan gagal apabila arahan suntikan kelihatan meyakinkan. Oleh itu, kawalan tersebut perlu dilaksanakan dalam sistem pengendalian atau rangkaian.

Adakah pemboleh ubah persekitaran benar-benar begitu berisiko untuk rahsia ejen?

Pemboleh ubah ini berisiko dalam satu perkara khusus: pemboleh ubah itu diwarisi. Setiap proses anak yang dimulakan oleh ejen menerima salinannya, termasuk skrip binaan, pelaksana ujian dan mana-mana hook pemasangan pakej. Pemboleh ubah itu juga boleh dibaca melalui /proc/<pid>/environ oleh pengguna yang sama. Oleh itu, apa-apa sahaja yang dijalankan oleh ejen boleh membacanya tanpa ejen menghantarnya. Fail yang dibaca hanya pada masa penggunaan, dengan LoadCredential= atau gateway, mengehadkan pendedahan kepada masa tersebut.

Adakah menyimpan rahsia dalam vault menyelesaikan masalah ini dengan sendirinya?

Hanya sebahagian. Vault menyelesaikan masalah penyimpanan. Jika anda mengehos vault itu sendiri, vault tersebut memerlukan proses pengukuhan keselamatan tersendiri, kerana pelayan Vaultwarden biasanya diceroboh melalui token pentadbir atau fail sandarannya, bukan melalui item yang disulitkan di dalamnya. Vault tidak menyelesaikan langkah terakhir, iaitu apabila sesuatu mengambil rahsia daripada vault dan menyerahkannya kepada ejen sebagai pemboleh ubah persekitaran. Keadaan itu mengembalikan anda kepada masalah asal. Perkara yang penting ialah pihak yang melakukan penggantian nilai tersebut. Jika ejen mendapatkan rahsia itu, ejen memiliki rahsia tersebut. Jika gateway atau sistem init melakukan penggantian di luar proses ejen, ejen tidak pernah memegangnya.

Bagaimanakah saya tahu sama ada ejen telah membocorkan sesuatu?

Biasanya, anda tidak dapat mengetahuinya selepas kejadian. Itulah sebabnya gateway diperlukan. Tanpa gateway, bukti anda berselerak dalam sejarah shell, transkrip ejen dan log sambungan keluar yang mungkin tidak anda simpan. Dengan gateway kelayakan, setiap penggunaan kelayakan direkodkan pada satu baris bersama identiti ejen dan cap masa. Jika anda mengesyaki kebocoran, putarkan kunci dahulu dan lakukan siasatan kemudian. Putaran kunci murah, manakala kepastian tidak.

Apakah perkara minimum yang perlu saya lakukan hari ini?

Pindahkan setiap fail .env keluar daripada direktori yang digunakan oleh ejen anda, dan cipta seorang pengguna tanpa keistimewaan bagi setiap ejen. Dua perubahan ini mengambil masa kira-kira sepuluh minit dan menutup laluan yang paling biasa, iaitu ejen membaca fail kelayakan yang tidak sepatutnya berada bersebelahan dengan kod. Gateway dan token berjangka pendek ialah langkah seterusnya, bukan langkah pertama. Titik permulaan yang sama terpakai pada mana-mana masa jalan ejen, termasuk menjalankan ejen autonomi dengan selamat pada VPS.

Adakah artikel ini berguna?