Belajar Tailwind CSS - Handling Dynamic Content & Security Considerations
Episode 11 of 23

Belajar Tailwind CSS - Handling Dynamic Content & Security Considerations

Episode ini membahas risiko kelas dinamis dari input pengguna, pola validasi dan safelist yang aman, serta cara menghindari injection via class names. Kalian juga belajar strategi content scanning untuk environment multi-tenant dengan isolasi yang benar.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Konten dinamis adalah keniscayaan di aplikasi nyata: warna dari database, ukuran dari preferensi pengguna, tema dari konfigurasi. Tapi cara kalian menggabungkan nilai itu ke class attribute membawa dua risiko: kelas tidak terbangkitkan oleh JIT, dan yang lebih serius — CSS injection lewat class name. Episode 11 membahas keduanya.

Risiko pertama membuat UI tampil tanpa gaya; risiko kedua bisa menjadi celah keamanan. Tailwind mendukung nilai arbitrer seperti p-[calc(100%-1rem)], dan jika nilai itu datang dari pengguna tanpa validasi, pengguna bisa menyuntikkan arbitrary value berbahaya ke dalam stylesheet kalian.

Risiko Kelas Dinamis dari User Input

Pola paling bermasalah adalah merangkai nama kelas dari potongan string:

JSPola yang berbahaya
const size = userInput; // nilai dari database atau form
const className = "p-" + size; // berbahaya

Dua masalah muncul: JIT tidak melihat string lengkap p-4 di content, jadi kelas tidak dibangkitkan; dan jika size berisi nilai arbitrer, pengguna bisa menyuntikkan aturan CSS sembarangan melalui fitur arbitrary values. Hindari pola ini sepenuhnya.

Validasi dan Safelist Patterns

Gantikan perangkaian dengan mapping eksplisit — setiap nilai yang diizinkan memetakan ke string kelas yang sudah ditulis lengkap:

JSMapping nilai ke kelas lengkap
const sizeMap = {
  small: "p-2 text-sm",
  medium: "p-4 text-base",
  large: "p-6 text-lg",
};
 
const className = sizeMap[size] ?? sizeMap.medium;

String kelas seperti "p-4 text-base" muncul utuh di content, sehingga JIT membangkitkannya. Nilai yang tidak dikenal jatuh ke default sizeMap.medium. Pola sizeMap[size] ?? sizeMap.medium ini aman, deterministik, dan mudah diuji.

Jika mapping tidak memungkinkan karena jumlah kombinasi besar tapi terbatas, gunakan safelist:

JSSafelist untuk set terbatas
safelist: [
  "bg-red-500",
  "bg-green-500",
  "bg-blue-500",
  "text-red-500",
  "text-green-500",
  "text-blue-500",
],

Safelist memaksa kelas tersebut selalu dibangkitkan. Batasi penggunaannya — setiap entri menambah ukuran CSS tanpa peduli dipakai atau tidak.

Menghindari Injection via Class Names

CSS injection terjadi saat nilai pengguna masuk ke class attribute dan Tailwind menerimanya sebagai arbitrary value. Dua lapis pertahanan:

  • Allowlist nilai: hanya terima nilai yang ada di mapping, tolak sisanya.
  • Validasi sebelum render: pastikan nilai tidak memuat karakter pembentuk aturan seperti [, :, dan ;.

Contoh validasi sederhana:

JSValidasi input sebelum dipakai
const allowedSizes = new Set(["small", "medium", "large"]);
const safeSize = allowedSizes.has(size) ? size : "medium";

allowedSizes.has(size) ? size : "medium" menjamin hanya tiga nilai yang pernah sampai ke markup. Tidak ada jalan bagi arbitrary value dari pengguna untuk masuk ke class attribute.

Warning

Jangan pernah menempatkan data pengguna langsung ke dalam class attribute. Jika aplikasi kalian memang harus menerima nama kelas dari luar, validasi terhadap allowlist dulu, lalu render sebagai string lengkap — bukan potongan yang dirangkai.

Content Scanning untuk Multi-tenant

Di environment multi-tenant, satu instance Tailwind melayani banyak tenant dengan tema berbeda. Tantangannya: jika seluruh tenant di-scan bersama, semua kelas semua tenant masuk ke satu stylesheet — boros dan berisiko bocornya token antar tenant.

Strategi yang direkomendasikan:

  • Build per tenant: jalankan Tailwind dengan content dan config khusus per tenant, hasilkan stylesheet terpisah per tenant.
  • Gunakan satu design token surface: batasi kelas yang bisa dipakai tenant ke set yang sudah di-safelist, sehingga stylesheet seragam dan aman.
  • Hindari arbitrary values dari input tenant: arbitrary values harus selalu berasal dari konfigurasi server, bukan permintaan klien.

Contoh build per tenant di CLI:

Build stylesheet per tenant
npx tailwindcss -i ./themes/tenant-a.css -o ./dist/tenant-a.css
npx tailwindcss -i ./themes/tenant-b.css -o ./dist/tenant-b.css

Setiap tenant mendapat file CSS terpisah yang hanya memuat kelas yang diizinkan untuknya — hasil scanning yang benar-benar terisolasi.

Di environment multi-tenant dengan SSR atau API-driven, pastikan daftar content hanya memuat file yang benar-benar menulis nama kelas — template dan komponen, bukan data runtime. Memindai isi database menambah beban build tanpa manfaat, karena JIT hanya mengenali string literal di source, bukan nilai yang disuntikkan dari luar.

Penutup

Episode 11 menutup celah keamanan dan keandalan konten dinamis: mapping eksplisit menggantikan perangkaian nama kelas, safelist untuk set terbatas, validasi allowlist untuk menghalau injection, dan build per tenant untuk isolasi di multi-tenant.

Inti yang harus dibawa pulang:

  • Jangan pernah merangkai nama kelas dari potongan string.
  • Mapping eksplisit membuat JIT melihat string lengkap dan membatasi nilai.
  • Safelist hanya untuk set kelas yang terbatas dan diketahui.
  • CSS injection dicegah dengan validasi allowlist sebelum render.
  • Multi-tenant sebaiknya di-build per tenant dengan token yang dibatasi.
  • Arbitrary values harus berasal dari konfigurasi server, bukan klien.

Di episode 12 selanjutnya kita akan membahas delivery & CDN — perbandingan build-time generation dengan Play CDN, keamanan CSP dan SRI saat memakai CDN, serta strategi caching dan HTTP headers untuk file CSS statis.

Belajar Tailwind CSS - Handling Dynamic Content & Security Considerations | Belajar Tailwind CSS