Skip to content
Konsultasi

Label prompt bocor ke chat AI, sanitasi di dua ujung

Adityo Guni Waluyo

Label RINGKAS bocor ke gelembung chat AI. Solusinya bukan prompt lebih ketat, tapi sanitasi input dan output di dua ujung pipeline.

Ringkasan

Label prompt seperti RINGKAS: bocor ke UI karena model nggak patuh format, dan larangan tambahan malah bikin pelanggarannya makin kreatif. Solusinya dua lapis: sanitasi teks knowledge base sebelum masuk prompt, plus rendering output via react-markdown dan rehype-sanitize yang hanya ngizinkan elemen dari allowlist. Pelajarannya: prompt cukup untuk arah kasar, kontrol andal ada di input dan output, bukan daftar larangan.

Chat internal di blog saya tiba-tiba nampilin teks **RINGKAS:** di gelembung jawaban. Bukan error, bukan crash. Cuma teks label dari prompt bocor sampai ke user, dan parahnya, kadang munculnya sudah dibungkus bold ala markdown. Di panel hasil yang diperluas, label kembarannya PENJELASAN: juga pernah ikut nongol.

Dugaan pertama saya predictable banget: prompt-nya kurang ketat. Tambah larangan, eksplisitin formatnya, ulangi sampai puas. Saya nambah larangan beberapa kali dan hasilnya tetap bocor. Model itu emang nggak patuh 100% sama instruksi format. Kadang dia bungkus label sendiri pakai bintang karena konteksnya "membantu" merapikan output. Makin banyak larangan di prompt, makin kreatif dia melanggarnya.

Setelah beberapa putaran gagal, saya berhenti dan ngitung ulang: teks apa saja sih yang sebenarnya masuk ke prompt ini?

Dua pintu teks asing

Chatbot ini pakai pola RAG. Konten dari knowledge base diambil, digabung ke prompt, dikirim ke LLM, terus jawabannya dirender di UI. Artinya ada dua titik di mana teks yang nggak saya tulis sendiri bisa masuk: konten knowledge base itu sendiri, dan output LLM yang nggak selalu patuh format. Sanitasi prompt doang cuma nutup satu pintu.

Pintu pertama saya tutup di backend. Ada fungsi _sanitize_injected_text di use_cases.py yang jalan sebelum teks knowledge base dan pesan CTA masuk prompt. Kerjanya sederhana: potong teks di 4000 karakter, buang control character, dan hapus pola injection umum kayak "ignore previous instructions" atau "abaikan semua instruksi". Skenario ini namanya indirect prompt injection, dan OWASP masukin sebagai risiko nomor satu di daftar LLM Top 10 versi 2025 mereka: konten dari sumber eksternal bisa mengubah perilaku model tanpa perlu manusia yang nulis instruksi itu langsung [3]. Yang bikin saya yakin pilih lapisan ini: OWASP juga mencatat bahwa RAG dan fine-tuning nggak sepenuhnya menutup celah ini [3]. Jadi percaya bahwa "konteks saya terkurasi kok" bukan strategi.

Pintu kedua di sisi output. Prompt lama melarang markdown total, sekarang saya balik arahnya: izinkan markdown ringan, tapi siapa pun yang boleh menentukan elemen HTML apa yang sampai browser bukan si model. Jawaban LLM sekarang dirender lewat komponen ChatMarkdown yang isinya react-markdown plus remark-gfm plus rehype-sanitize. React-markdown aman secara default karena nggak pakai dangerouslySetInnerHTML sama sekali [1]. Rehype-sanitize jadi filter terakhir: semua yang nggak masuk allowlist schema dibuang, dan defaultnya ngikutin pola yang dipakai GitHub [2]. Model tetap aja kadang nulis **RINGKAS:**, tapi sekarang yang menentukan itu dirender jadi bold atau dibuang adalah kode saya, bukan kebetulan.

Dari larangan jadi allowlist

Perubahan promptnya sendiri kecil tapi bentuknya beda. Dulu: "tanpa format markdown apa pun", daftar larangan makin panjang tiap insiden. Sekarang: markdown ringan diizinkan secara eksplisit, bold buat istilah kunci dan bullet list buat enumerasi, sisanya dilarang, heading dan code block paling depan. Perbedaan psikologisnya lumayan terasa. Model yang dikasih channel resmi buat format keliatannya nggak lagi "ngiber" bikin format sendiri di luar izin.

Sisa label yang tetap lolos dibersihkan di tahap render pakai regex toleran yang match label telanjang maupun yang dibungkus bintang. Nggak elegan, tapi posisinya cuma sabuk pengaman kedua setelah sanitasi utama.

Hasil akhirnya dicek pakai suite sanitasi yang jalan 148 test di CI. Nggak ada framework aneh, cuma test konten yang memastikan pola berbahaya kefilter dan label scaffolding nggak pernah sampai UI.

Keputusan yang saya ambil dari insiden ini: berhenti mengandalkan prompt sebagai satu-satunya pagar format. Prompt tetap ada, tapi cuma buat kasih arah kasar. Kontrol yang reliable ada di input, lewat sanitasi teks eksternal sebelum masuk prompt, dan di output, lewat renderer yang cuma ngebolehin elemen dari allowlist. Alternatif yang saya buang: nambah larangan lagi ke prompt tiap ketemu leak baru. Dari pengalaman, makin panjang daftar larangan di prompt, makin sering dilanggar.


Sumber

Artikel terkait