Case Study

Studi Kasus Atmo LMS: Trusted Types API Pangkas DOM XSS Finding dari 11 ke 0 dalam 21 Hari di 2026

Vito Atmo
Vito Atmo·27 Mei 2026·0 kali dibaca·5 min baca
Studi Kasus Atmo LMS: Trusted Types API Pangkas DOM XSS Finding dari 11 ke 0 dalam 21 Hari di 2026

TL;DR: Audit keamanan Q1 2026 di Atmo LMS menemukan 11 DOM-based XSS finding lewat Quill rich text editor dan widget komentar. Setelah Vito Atmo memasang Trusted Types API secara bertahap (report-only 14 hari, enforcing hari ke-22), seluruh finding hilang tanpa keluhan pengguna atau drop di engagement halaman pembelajaran.

Pada Februari 2026, tim Atmo LMS menerima laporan audit keamanan dari pihak ketiga. Hasilnya mengejutkan: 11 finding DOM-based XSS di tiga modul utama. Tujuh di antaranya berasal dari Quill rich text editor yang dipakai instruktur menulis materi, tiga dari widget komentar mahasiswa, dan satu dari preview HTML email broadcast.

Yang lebih mengkhawatirkan, CSP yang sudah aktif penuh tidak menangkap satupun. Semua eksploitasi terjadi lewat sink internal seperti innerHTML dan dangerouslySetInnerHTML React yang menerima string mentah dari user-generated content. Lihat juga Content Security Policy.

Konteks Aplikasi

Atmo LMS adalah Learning Management System internal yang dipakai 4.200 mahasiswa aktif dan 180 instruktur. Stack utama Next.js 15 + Supabase. Tiga sink berbahaya teridentifikasi:

SinkModulSeverity
dangerouslySetInnerHTMLQuill content renderHigh (7 finding)
innerHTMLKomentar mahasiswaMedium (3 finding)
document.write di preview iframeEmail broadcastCritical (1 finding)

Mengapa CSP Tidak Cukup

CSP standar memblokir loading script eksternal yang tidak whitelist. Tapi untuk DOM XSS, payload sudah ada di dalam string yang valid dari sisi CSP (tidak ada <script> tag eksternal). Payload klasik <img src=x onerror=alert(document.cookie)> lolos CSP script-src 'self' karena <img> bukan kategori script.

Praktik standar di industri menunjukkan DOM XSS mengisi mayoritas finding sisa setelah CSP aktif. Untuk SPA yang sering memanipulasi DOM dengan data tidak tepercaya, Trusted Types API adalah lapisan pelengkap wajib.

Rencana Implementasi 21 Hari

Vito Atmo memetakan rollout jadi tiga fase:

Fase 1: Report-Only Mode (Hari 1-14)

Header Content-Security-Policy-Report-Only ditambahkan di middleware Next.js:

ts
response.headers.set(
  'Content-Security-Policy-Report-Only',
  "require-trusted-types-for 'script'; trusted-types default quill-policy comment-policy; report-uri /api/csp-report"
);

Tujuannya: kumpulkan data violation real tanpa break aplikasi. Selama 14 hari, endpoint /api/csp-report menerima 847 laporan unik. Setelah dikelompokkan, hanya 23 lokasi kode yang melanggar (banyak duplikasi dari Quill yang rerender).

Fase 2: Patch Sink (Hari 15-21)

Tiga policy didaftarkan:

ts
trustedTypes.createPolicy('quill-policy', {
  createHTML: (s) => DOMPurify.sanitize(s, {ALLOWED_TAGS: ['p','b','i','ul','ol','li','a','img']})
});
trustedTypes.createPolicy('comment-policy', {
  createHTML: (s) => DOMPurify.sanitize(s, {ALLOWED_TAGS: ['p','br','a'], ALLOWED_ATTR: ['href']})
});
trustedTypes.createPolicy('default', {
  createHTML: (s) => DOMPurify.sanitize(s)
});

Tiap policy punya allowlist HTML tag berbeda sesuai konteks. Quill butuh tag rich text, komentar hanya butuh teks dan link. Pemisahan ini mencegah pengguna komentar menyusupkan <img> yang bisa dipakai tracking pixel tidak resmi.

Fase 3: Enforcing (Hari 22)

Pindah header dari Report-Only ke Content-Security-Policy mengaktifkan enforcing. Audit ulang Acunetix hari ke-25 menunjukkan 0 finding DOM XSS.

Hasil Pasca-Implementasi

Metrik KeamananSebelumSesudah
DOM XSS finding (Acunetix)110
Burp Suite Active Scan6 issues0 issues
Manual penetration test4 exploit success0 exploit success
Metrik UXSebelumSesudah
Quill editor load time380 ms412 ms (+32 ms)
Komentar submission rate14 per hari14 per hari
Halaman materi engagement8 menit 14 detik8 menit 9 detik
Keluhan support terkait fitur0 baseline2 keluhan (resolved hari 2)

Penambahan 32 ms di Quill load berasal dari DOMPurify (18 KB gzipped) yang dieksekusi. Dari pengamatan Web Vitals field data, dampak ke INP sangat kecil, masih dalam zona "good".

Dua keluhan support muncul di hari 1 dan 2: instruktur tidak bisa paste table dari Microsoft Word. Solusinya: tambahkan 'table','tr','td','th' ke ALLOWED_TAGS policy Quill. Resolved dalam 6 jam.

Pelajaran Penting

Dari pengalaman implementasi ini, ada tiga catatan untuk tim lain:

Pertama, jangan langsung enforcing. Report-only mode wajib minimal 14 hari supaya data violation lengkap. Aplikasi besar punya banyak edge case yang baru muncul di traffic real.

Kedua, policy per modul, bukan satu policy global. Quill butuh tag berbeda dari komentar. Default policy paling sederhana, modul khusus lebih ketat. Lihat juga Sec-Fetch Headers sebagai pelengkap di sisi server.

Ketiga, siapkan komunikasi internal. Instruktur dan moderator perlu diberi tahu bahwa beberapa fitur paste akan terbatas. Kirim memo 3 hari sebelum enforcing rilis. Dokumentasi lengkap tersedia di web.dev Trusted Types.

Pertanyaan Umum

Apakah ROI implementasi Trusted Types sebanding?

Untuk Atmo LMS dengan 4.200 mahasiswa aktif yang menyimpan data pembelajaran sensitif, satu insiden XSS yang berhasil bisa berarti reset password massal dan churn 5-10 persen. Investasi 21 hari engineering jauh lebih murah.

Apakah React 19 menambah kerumitan?

React 19 sudah punya integrasi Trusted Types native lewat policy react-renderer. Tidak perlu daftar manual. Tapi dangerouslySetInnerHTML tetap butuh wrapping eksplisit.

Bagaimana kalau pengguna pakai browser lawas?

Browser yang tidak support Trusted Types akan mengabaikan header require-trusted-types-for. Aplikasi tetap berfungsi normal di Safari 16 dan Firefox 119 ke bawah, tanpa proteksi tambahan. Untuk audit, persentase pengguna browser tersebut dikalkulasi dari GA4. Di Atmo LMS, persentasenya 4 persen.

Apakah ini menggantikan code review?

Tidak. Trusted Types adalah jaring pengaman, bukan pengganti review. Review tetap dilakukan, tapi human error yang tidak tertangkap reviewer akan ditolak browser sebagai lapisan kedua.

Penutup

Setelah enforcing aktif, tim engineering Atmo LMS punya kebiasaan baru: setiap PR yang menyentuh sink DOM otomatis ditest di staging dengan CSP report-only aktif. Jika ada violation, PR ditolak sebelum merge. Trusted Types tidak hanya menutup celah, tapi juga mengubah pola kerja jadi lebih defensif.

Bagikan

Artikel Terkait

#trusted-types#atmo-lms#dom-xss#keamanan-web#case-study

Butuh website yang benar-benar bekerja?

Hubungi Vito untuk konsultasi gratis 15 menit.

WhatsApp Sekarang