Bug injeksi HTML tersimpan (stored HTML injection) pada papan lowongan kerja RamenHire memungkinkan siapa saja mengisi formulir publik dan mengubah email notifikasi tim menjadi platform phishing.
Bagaimana bug tersebut masuk
RamenHire berjalan di atas Next.js dan Supabase serta memungkinkan startup memasang lowongan kerja tanpa perlu masuk (login). Setiap pengajuan memicu pengiriman email ke tim perekrutan internal. Isi email disusun dengan menggabungkan kolom formulir mentah—nama perusahaan, deskripsi pekerjaan, nama kontak—secara langsung ke dalam string HTML. Tidak ada proses escaping atau sanitasi yang dilakukan sebelum string tersebut mencapai layanan email.
Karena templat tersebut memperlakukan input pengguna sebagai HTML yang aman, pelamar yang berniat jahat dapat menyuntikkan tautan palsu "klik di sini untuk verifikasi". Payload tersebut tersimpan di server sebagai bagian dari daftar lowongan kerja, sehingga setiap email notifikasi berikutnya akan membawa kode berbahaya tersebut. Penyerang dapat menyembunyikan tautan itu di samping tombol "View Dashboard" yang asli, menipu anggota tim agar membocorkan kredensial atau mengunjungi situs phishing.
Mengapa ini penting
Email-email tersebut dikirim ke kru perekrutan inti, orang-orang yang terus-menerus mengklik tautan untuk memproses kandidat melalui alur kerja (pipeline).
Celah teknis
Escaping HTML bukanlah satu langkah tunggal. Ada dua konteks yang perlu dilindungi:
- Text nodes – konten di antara tag. Mengonversi
<menjadi<dan>menjadi>dapat menghentikan tag, tetapi tidak menghentikan penyerang untuk keluar dari nilai atribut. - Attribute values – string di dalam tag seperti
href="…". Menyuntikkan tanda kutip ("atau') memungkinkan penyerang menutup atribut lebih awal dan memasukkan markup mereka sendiri.
Kode aslinya hanya menangani teks biasa, sehingga injeksi atribut terbuka lebar.
Memperbaiki masalah
Pengembang menambahkan fungsi pembantu (helper) escapeHtml kecil dan menerapkannya pada setiap nilai yang diberikan pengguna sebelum interpolasi, baik nilai tersebut berakhir di text node maupun di atribut.
Langkah-langkah verifikasi
- Local testing – Serangkaian string injeksi dimasukkan ke dalam fungsi templat. Setiap pengujian hanya menunjukkan karakter yang telah di-escape, tanpa ada tag yang dapat dieksekusi.
- Production testing – Setelah menerapkan patch, sebuah payload dikirimkan melalui situs langsung, melewati pemeriksaan bot Cloudflare Turnstile. Email yang dihasilkan masuk ke kotak masuk pengembang; tautan berbahaya dan tag skrip apa pun muncul sebagai teks acak, bukan elemen yang dapat diklik.
Catatan tentang Gmail: layanan tersebut mungkin tetap mewarnai URL menjadi biru dan membuatnya dapat diklik, tetapi itu adalah kenyamanan di sisi klien (client-side) dan tidak berarti injeksi HTML berhasil. Perbaikan tersebut menghentikan injeksi dari membuat tombol asli atau menjalankan skrip.
Apa yang harus diperhatikan pengembang
- Jangan pernah percaya data dari formulir publik – Bahkan formulir yang tampak tidak berbahaya dapat menghasilkan output tersimpan yang digunakan di tempat lain.
- Lakukan escaping pada saat penggunaan – Terapkan escaping yang sadar konteks (context-aware) (teks vs. atribut) tepat sebelum perenderan, bukan lebih awal dalam alur kerja (pipeline).
- Uji sisi klien dan sisi server – Unit test dapat menangkap kegagalan yang nyata; pengujian end-to-end manual melalui UI langsung memastikan pertahanan tetap kuat di bawah lalu lintas nyata.
- Waspadai keunikan klien email – Beberapa klien secara otomatis menautkan URL, sehingga menciptakan rasa aman yang palsu. Verifikasi bahwa HTML yang mendasarinya tidak mengandung elemen aktif.
Intinya
Satu kelalaian dalam menangani input pengguna mengubah email notifikasi rutin menjadi vektor phishing. Penerapan rutinitas HTML-escaping yang komprehensif serta validasi perbaikan secara lokal dan di produksi telah memulihkan integritas kotak masuk. Insiden ini menekankan pelajaran abadi bagi aplikasi web apa pun yang menghasilkan HTML dari data yang tidak tepercaya: sanitasi yang tepat tidak dapat ditawar, dan mengabaikannya dapat membuka jalur langsung ke kotak masuk pengguna Anda.
