Skip to main content
BlogSecurity

Sentry MCP Prompt Injection: Panduan Bertahan dari Agentjacking

Bagaimana error event palsu di Sentry menjadi jalur perintah ke Claude Code, Cursor dan Codex, apa yang terbukti dan tidak, serta kontrol yang benar-benar bekerja.

Matthews Wong

Software Engineer

Oktober 202612 menit baca
Sentry MCP Prompt Injection: Panduan Bertahan dari Agentjacking

Ringkasan Utama

Agentjacking adalah indirect prompt injection: penyerang memakai DSN Sentry publik untuk mengirim error event palsu, lalu AI coding agent yang membacanya lewat Sentry MCP server menjalankan perintah di bagian Resolution. Tenet melaporkan tingkat keberhasilan 85%. Bertahanlah dengan approval gate, sandbox, rotasi DSN, dan perlakukan telemetry sebagai input tidak tepercaya.

Detail yang membuat saya berhenti adalah betapa sedikit yang dibutuhkan penyerang. Tanpa phishing, tanpa login curian, tanpa pijakan di jaringan Anda: hanya DSN Sentry yang sudah dikirim frontend Anda sendiri ke setiap pengunjung. Saya membaca tiga laporan publik tentang serangan ini dengan teliti, bersama dokumentasi vendor dan dokumentasi agent yang menentukan apakah serangan itu berhasil. Isi artikel ini adalah pembacaan dari sisi defender.

Saya tidak mereproduksi serangan ini dan artikel ini tidak memuat payload; pertahanannya memang tidak membutuhkannya. Setiap angka di bawah dikaitkan dengan dokumen tempat saya membacanya, dan bila sumber saling berbeda atau tidak menjelaskan, saya katakan. Tujuannya adalah checklist hardening yang bisa Anda terapkan ke MCP server mana pun yang mengembalikan telemetry, bukan hanya milik Sentry.

Kunci write-only dan tool read-only yang dirantai

Menurut Tenet Security, penyerang hanya butuh DSN Sentry publik, kredensial yang menurut dokumentasi Sentry aman ditanam di JavaScript frontend, untuk mengirim error event sintetis. Pesan dan nama key di context memuat markdown yang dibuat menyerupai panduan diagnostik Sentry sendiri, dengan bagian Resolution yang berisi sebuah perintah. Ketika developer kemudian meminta agent meninjau issue Sentry, Sentry MCP server mengembalikan event itu berdampingan dengan event asli. Catatan riset CSA menyebut agent yang diuji menganggapnya otoritatif dan menjalankan perintah dengan hak akses sistem operasi developer.

Tidak satu pun dari kedua sisinya adalah bug. DSN memang dimaksudkan publik, dan MCP server hanya mengembalikan apa yang memang harus dikembalikan. Catatan CSA membingkai masalah ini sebagai masalah arsitektur, yang bertumpu pada tiga keputusan desain: ingest yang permisif, MCP yang meneruskan data tanpa pemeriksaan integritas konten, dan agent yang mempercayai output tool sama seperti instruksi operator. Kredensial yang berisiko menurut catatan itu adalah AWS key, token GitHub, kredensial Kubernetes, login registry dari .npmrc dan .docker/config.json, serta environment variable CI/CD.

Apa kata angkanya, dan apa yang tidak dijelaskan

Saya membaca post Tenet, catatan CSA, dan laporan Infosecurity Magazine. Angka-angka utamanya berulang di ketiganya, tetapi tidak satu pun menerbitkan data mentahnya. Tabel ini memperlihatkan jarak antara yang diklaim dan yang bisa Anda periksa.

KlaimYang dinyatakan sumberYang tidak saya temukan
Tingkat keberhasilan 85%Tenet melaporkan 100+ instance agent dalam gelombang validasi terkontrol dan tingkat eksploitasi 85%. CSA mengaitkannya dengan Claude Code, Cursor dan Codex CLI.Jumlah percobaan, denominator, rincian per agent, dan nama model. Satu-satunya versi yang disebut adalah Claude Code 2-1-161.
Minimal 2.388 organisasi tereksposDitemukan lewat pengintaian pasif: indeks Censys, code search, dan ekstraksi CDN loader. Terekspos berarti DSN valid yang bisa disuntik ditemukan. 71 di antaranya masuk Tranco top 1M.Berapa yang benar-benar diserang. Tenet menyebut 2.221 tidak masuk dalam validation set.
Sentry mengakui pada 3 Juni 2026Tenet dan CSA sama-sama menyebut disclosure dan balasan Sentry terjadi hari itu, dan keduanya mengutip 'technically not defensible'.Pernyataan dari Sentry sendiri. Kedua laporan menyampaikan balasan itu secara tidak langsung.
Tanggal publikasiCatatan CSA bertanggal 2026-06-14 dan mengutip Tenet pada 9 Juni. Infosecurity terbit 11 Juni. Halaman Tenet yang saya ambil bertanggal 17 Juni dengan judul berbeda.Tanggal mana yang kanonik. Saya mengutip lewat URL dan tidak bersandar pada urutan kejadian.
Pertahanan di level prompt gagalTenet menyebut pertahanan itu gagal dalam pengujiannya. Infosecurity melaporkan agent tetap menjalankan payload walau diminta mengabaikan data tidak tepercaya.Prompt apa saja yang dicoba.

Ini riset vendor dan dilaporkan sendiri, jadi saya tidak akan merencanakan sesuatu berdasarkan angka 85% persisnya. Saya akan merencanakan berdasarkan arahnya, yang sejalan dengan cara OWASP menjelaskan tool poisoning: respons yang dibaca model sebagai tepercaya padahal tidak ada yang memeriksanya. Anggap tingkatnya di atas nol dan rancang dari situ.

Mengapa DSN yang bocor bukan rahasia yang bocor

Penjelasan DSN dari Sentry lugas: DSN aman dibiarkan publik karena hanya mengizinkan pengiriman event baru dan data terkait, tanpa akses baca. Halaman yang sama mengakui risiko yang relevan di sini, yaitu siapa pun bisa mengirim event ke organisasi Anda dengan informasi apa saja, menyebutnya jarang terjadi, dan menunjuk ke pemblokiran IP serta opsi rotate dan revoke key di Client Keys (DSN) pada pengaturan project.

Saran itu ditulis untuk dunia di mana manusia membaca event di dashboard dan sampah hanyalah noise. Begitu agent yang punya shell membacanya, informasi apa saja mencakup instruksi. Halaman Sentry MCP yang saya baca menyebut koneksi memakai OAuth dan URL bisa dibatasi ke satu organisasi atau project, dengan rekomendasi scope project; halaman itu tidak punya bagian keamanan dan tidak menyinggung prompt injection. Scoping memperkecil jumlah telemetry tidak tepercaya yang ditarik agent. Ia tidak membuat telemetry itu menjadi tepercaya.

Respons Sentry, dan mengapa filter string bukan perbaikan

Menurut Tenet dan CSA, Sentry diberi tahu pada 3 Juni 2026, mengakuinya di hari yang sama, menyebut perbaikan menyeluruh di level platform 'technically not defensible', dan merilis content filter global untuk satu string payload yang muncul dalam riset. Ingat catatan dari tabel: saya membaca balasan itu secara tidak langsung.

CSA berargumen bahwa pencocokan string tidak menutup jalurnya, karena pengubahan kata atau rute tidak langsung bisa lolos. OWASP sepakat soal kesulitan dasarnya: mendeteksi instruksi suntikan di respons teks bebas secara penuh masih masalah terbuka. Posisi Sentry masuk akal, karena platform tidak bisa membedakan paragraf Resolution yang jahat dari yang sah tanpa merusak error reporting. Karena itu kontrolnya harus ada di tempat aksi terjadi, yaitu di runtime agent, yang juga menjadi rekomendasi utama Tenet.

Jangan menunggu perbaikan dari vendor. Content filter hanya melindungi dari payload yang sudah dipublikasikan seseorang. Anggap payload berikutnya berbeda kalimatnya, dan letakkan kontrol Anda pada perintahnya, bukan pada teksnya.

Kontrol mana yang bertahan, dan mana yang hanya terasa aman

Setiap kontrol di bawah menghentikan sesuatu yang nyata dan melewatkan hal tertentu. Kesalahan yang mahal adalah menumpuk beberapa kontrol yang gagal dengan cara yang sama, misalnya mempercayai approval prompt padahal aturan allow yang luas sudah menghilangkannya.

KontrolYang dihentikanYang terlewat
Prompt yang menyuruh mengabaikan data tidak tepercayaTidak ada yang bisa diandalkan.Tenet melihatnya gagal; Infosecurity melaporkan agent tetap menjalankan payload.
Persetujuan manusia untuk shell, instalasi, dan request keluarLangkah eksekusi, jika ada orang yang membaca perintahnya.'Don't ask again' di Claude Code bersifat permanen per repository dan perintah, sehingga satu persetujuan ceroboh menjadi allow tetap.
Aturan deny untuk curl dan wgetExfiltration lewat dua program itu.Dokumentasi menyebut aturan deny tidak cocok dengan program yang sama lewat path atau di dalam sh -c, dan pola argumen mudah dilewati.
Sandbox dengan network allowlist dan credential denyProses shell yang mengakses host di luar daftar atau membaca kredensial yang dilarang, walau injeksi melewati penilaian model.Ia hanya membungkus perintah shell. Tool file, MCP server, dan hook berjalan di luar sandbox.
Merotasi DSN yang tereksposPengiriman dengan key lama yang sudah dipanen.Key baru dikirim di frontend yang sama dan kembali publik begitu Anda deploy.
Menonaktifkan Sentry MCP serverSeluruh kanalnya.Anda kehilangan triage lewat agent, dan telemetry MCP berikutnya punya bentuk yang sama.

Satu-satunya baris yang bertahan terlepas dari keputusan model adalah sandbox, dan dokumentasi Claude Code menyatakannya: pembatasan sandbox tetap berlaku walau prompt injection melewati keputusan Claude. Baris permission adalah garis pertama Anda, sedangkan sandbox adalah yang tidak bergantung pada orang yang harus tetap awas pukul 6 sore.

File settings Claude Code yang menjaga langkah setelah membaca

Saya tidak akan memblokir proses membaca, karena triage adalah alasan Anda menghubungkan Sentry. Saya akan menjaga apa yang boleh dilakukan agent setelahnya. Dokumentasi permissions menyebut aturan dievaluasi deny, lalu ask, lalu allow, kecocokan pertama menang, dan aturan ask tetap memunculkan prompt walau ada allow yang lebih spesifik. Jadi ask pada npx tetap bertahan meski rekan tim menambahkan aturan allow di file settings lain.

// ~/.claude/settings.json or .claude/settings.json. Strip the comments: JSON has none.
{
  "permissions": {
    // ask is checked after deny and before allow, and it still prompts when a
    // narrower allow matches. A teammate's allow rule cannot quietly undo it.
    "ask": [
      "Bash(npx *)",
      "Bash(npm install *)",
      "Bash(npm i *)",
      "Bash(pip install *)"
    ],
    // Deny is a belt, not the boundary: it does not match the same program by
    // path or inside sh -c, so the sandbox allowlist below is what must hold.
    "deny": [
      "Bash(curl *)",
      "Bash(wget *)",
      "Read(./.env)",
      "Read(./secrets/**)"
    ]
  },
  "sandbox": {
    "enabled": true,
    "network": {
      // Starts empty. Every host here is a decision somebody made on purpose.
      "allowedDomains": ["github.com", "*.npmjs.org"]
    },
    "credentials": {
      "files": [
        { "path": "~/.aws/credentials", "mode": "deny" },
        { "path": "~/.ssh", "mode": "deny" }
      ],
      "envVars": [
        { "name": "GITHUB_TOKEN", "mode": "deny" },
        { "name": "NPM_TOKEN", "mode": "deny" }
      ]
    }
  }
}

// Wrong: "allow": ["Bash(npx *)"] because the prompt got tiresome. That is the
// standing allow an injected Resolution section is hoping for.
// Right: keep it under ask, and approve only commands you have read.

Tiga detail dari dokumentasi membentuk file ini. Jaringan sandbox dimulai dengan daftar allowed domains yang kosong, jadi setiap host yang Anda tambahkan adalah sebuah keputusan. Blok credentials menolak pembacaan file dan menghapus environment variable untuk perintah di dalam sandbox. Dan setiap aturan mcp__ yang memakai tanda kurung dilewati saat settings dimuat, sehingga parameter tool Sentry tidak bisa dibatasi di sini; jalankan claude doctor untuk melihat aturan yang dilewati. Saya hanya membaca dokumentasi Claude Code: Cursor dan Codex CLI butuh padanannya sendiri, yang belum saya verifikasi.

Hardening MCP apa pun yang mengembalikan telemetry

Bentuk masalahnya berlaku jauh di luar Sentry: log, trace, issue tracker, dan inbox dukungan semuanya membiarkan orang luar menulis teks yang kemudian dibaca agent. Saran Tenet adalah menginventarisasi tool MCP mana yang mengembalikan data tidak tepercaya atau dipengaruhi pihak luar, dan apa yang mencegah data itu memicu eksekusi kode. Daftar OWASP memberi jawabannya: skema respons yang tetap, tool berhak istimewa yang diisolasi dari server eksternal, pembatasan yang ditegakkan di execution layer bukan di system prompt, allowlist server, dan konfirmasi di luar konteks LLM.

Di sisi Sentry, CSA merekomendasikan agar pelaporan error dari klien dirutekan lewat relay di sisi server. Halaman tunnel Sentry menjelaskan mekanismenya: endpoint yang mem-parse envelope header, mencocokkan project ID dengan daftar yang diizinkan dan host dengan host Sentry Anda, lalu meneruskannya ke endpoint envelope. Halaman itu tidak mengklaim DSN hilang dari klien, dan sketsa saya di bawah hanya mencegah relay Anda menjadi open proxy. Pemegang DSN tetap bisa mengirim langsung ke ingest. Menutupnya butuh rotasi, atau relay yang menyimpan DSN sendiri.

// tunnel.mjs  (Node 22+)  Run: node tunnel.mjs
// The checks Sentry's tunnel docs describe: parse the envelope header, verify
// project and host, forward to the envelope endpoint.
import { createServer } from "node:http";

const SENTRY_HOST = "o0000000.ingest.sentry.io"; // placeholder: your own ingest host
const ALLOWED_PROJECT_IDS = new Set(["0000000"]); // placeholder: your project ids
const MAX_BODY_BYTES = 200000; // my cap, not Sentry's

createServer(async (req, res) => {
  if (req.method !== "POST") { res.writeHead(405).end(); return; }
  const chunks = [];
  let size = 0;
  for await (const chunk of req) {
    size += chunk.length;
    if (size > MAX_BODY_BYTES) { res.writeHead(413).end(); return; }
    chunks.push(chunk);
  }
  const body = Buffer.concat(chunks);
  // The first line of an envelope is its header, and it carries the dsn.
  const newline = body.indexOf(10);
  let dsn;
  try {
    dsn = new URL(JSON.parse(body.subarray(0, newline).toString("utf8")).dsn);
  } catch {
    res.writeHead(400).end();
    return;
  }
  const projectId = dsn.pathname.replace("/", "");
  if (dsn.hostname !== SENTRY_HOST || !ALLOWED_PROJECT_IDS.has(projectId)) {
    res.writeHead(403).end(); // otherwise this relay is an open proxy
    return;
  }
  const upstream = await fetch("https://" + SENTRY_HOST + "/api/" + projectId + "/envelope/", {
    method: "POST",
    body,
  });
  res.writeHead(upstream.status).end();
}).listen(8787);

// What this does NOT do: stop someone who holds the DSN from posting straight
// to ingest. Only rotation, or a relay that keeps the DSN server-side, does.

Batasi URL Sentry MCP ke satu project, seperti rekomendasi Sentry, dan beri tiap agent hanya project yang ia kerjakan. Cakupan yang lebih kecil berarti lebih sedikit event yang bisa ditulis penyerang sampai ke model.

Yang dikerjakan hari Senin

Inilah urutan yang akan saya kerjakan, dari yang termurah dan paling bernilai. Tidak ada yang menunggu vendor merilis sesuatu.

  1. Inventarisasi setiap MCP server yang bisa dijangkau agent Anda dan tandai yang outputnya bisa ditulis orang luar: error tracker, log, tiket, inbox.
  2. Cari DSN di frontend hasil build dan repository dengan perintah di bawah, lalu rotate dan revoke key yang terekspos di Client Keys (DSN), dijadwalkan bersama sebuah deploy.
  3. Terapkan file settings di atas lewat managed atau project settings: ask untuk package runner dan instalasi, deny untuk curl dan wget, sandbox aktif dengan credential deny.
  4. Batasi URL Sentry MCP ke satu project, atau hapus server itu dari agent yang tidak melakukan triage error.
  5. Pindahkan kredensial plaintext dari lokasi yang bisa dibaca proses agent, seperti saran CSA, dan blokir alamat metadata cloud 169.254.169.254 dari sandbox.
  6. Jalankan satu latihan red-team di project uji: kirim event palsu yang tidak berbahaya dengan teks remediation yang meminta agent menulis file penanda, lalu pastikan agent berhenti untuk bertanya terlebih dahulu.
# 1. DSNs shipped in your build output and tracked files. Sentry's docs give the
#    shape: https://<key>@o<orgId>.ingest.sentry.io/<projectId>
grep -rEoh 'https://[0-9a-f]+@o[0-9]+[.]ingest[.a-z]*[.]sentry[.]io/[0-9]+' \
  dist build .next/static 2>/dev/null | sort -u
git grep -nE 'https://[0-9a-f]+@o[0-9]+[.]ingest[.a-z]*[.]sentry[.]io/[0-9]+'

# 2. Confirm the settings file loaded and nothing was skipped. The docs say
#    claude doctor lists any rule that was dropped.
claude doctor

Langkah keenam adalah satu-satunya yang menguji seluruh rantai, bukan satu lapisan, dan hanya butuh satu sore. Jika agent menulis file penanda tanpa bertanya, approval gate Anda hanya hiasan.

Aturan yang saya ambil dari kasus ini: teks apa pun yang bisa ditulis orang luar adalah tidak tepercaya, bahkan bila datang lewat tool pilihan Anda dan kredensial yang bekerja sesuai desain. Letakkan kontrol pada apa yang boleh dilakukan agent berikutnya, tegakkan di bawah level model, dan anggap content filter vendor sebagai bonus, bukan batas keamanan.

FAQ

Pertanyaan Umum

Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.

Apa itu agentjacking pada serangan Sentry MCP?

Itu adalah indirect prompt injection yang dilaporkan Tenet Security. Penyerang memakai DSN Sentry publik untuk mengirim error event palsu, dan ketika AI coding agent membacanya lewat Sentry MCP server, agent bisa menjalankan perintah yang tersembunyi di teks Resolution. Tidak ada phishing atau login curian yang terlibat.

Bisakah seseorang menyerang saya hanya dengan DSN Sentry saya?

Sentry mendokumentasikan bahwa DSN hanya mengizinkan pengiriman event tanpa akses baca, dan mengakui bahwa siapa pun bisa mengirim event dengan isi apa saja. Riset menyebut itu cukup untuk menjangkau agent yang kemudian membaca event tersebut. Apakah perintah benar-benar berjalan bergantung pada pengaturan approval dan sandbox agent Anda.

Apakah Sentry sudah memperbaiki masalah agentjacking?

Menurut Tenet dan catatan riset CSA, Sentry mengakui laporan pada 3 Juni 2026 tetapi menyebut perbaikan platform menyeluruh technically not defensible. Sentry merilis content filter untuk string payload tertentu yang muncul di riset. CSA berargumen filter string tidak menutup jalur yang mendasarinya.

Bagaimana cara mencegah Claude Code menjalankan perintah dari hasil MCP?

Pasang aturan ask untuk package runner dan instalasi, deny untuk curl dan wget, lalu aktifkan sandbox dengan network allowlist dan credential deny. Claude Code mengevaluasi deny, lalu ask, lalu allow, dan dokumentasinya menyatakan batas sandbox tetap berlaku bila injeksi melewati model. Pakai 'don't ask again' hanya untuk perintah yang sudah Anda tinjau.

Apakah merotasi DSN Sentry mencegah serangan ini?

Merotasi dan me-revoke key yang terekspos menghentikan siapa pun yang memakai key lama yang sudah dikumpulkan. DSN baru tetap dikirim di frontend Anda, jadi kembali publik setelah deploy berikutnya. Rotasi mengurangi eksposur tetapi tidak menggantikan approval gate dan sandbox pada agent.