Episode ini membahas keamanan aplikasi Remix: secure headers dan content security policy, pencegahan XSS, CSRF, dan injection, validasi input dengan sanitasi di server, serta penanganan secret dan kredensial dengan aman.

Autentikasi di episode 11 membuka pintu aplikasi kalian. Tapi pintu saja tidak cukup — jendela, ventilasi, dan lantai bawah juga harus diperiksa. Episode 12 adalah audit keamanan menyeluruh untuk aplikasi Remix.
Kabar baiknya, Remix sudah aman secara default dalam banyak hal. Rendering React melakukan escaping otomatis sehingga XSS dari data biasa jarang terjadi. Action berbasis form memakai cookie sameSite untuk melawan CSRF. Tapi default yang baik bukan alasan untuk lengah — keamanan adalah lapisan yang dibangun sadar.
Episode 12 akan membahas secure headers dan CSP, pencegahan XSS, CSRF, dan injection, validasi dan sanitasi input, serta penanganan secret yang benar.
Header HTTP memberi sinyal pada browser untuk memblokir perilaku berbahaya. Di Remix, header diatur lewat fungsi headers pada route, atau lebih baik lagi, secara global di root:
export function headers() {
return {
"X-Frame-Options": "DENY",
"X-Content-Type-Options": "nosniff",
"Referrer-Policy": "strict-origin-when-cross-origin",
"Permissions-Policy": "camera=(), microphone=()",
};
}Header ini memblokir framing, mencegah MIME sniffing, dan membatasi data yang dikirim lewat referrer. Mulailah dari empat header ini — semuanya didukung browser modern tanpa risiko besar.
CSP memberi daftar sumber yang diizinkan untuk script, style, dan aset. Ini pertahanan terkuat melawan XSS. CSP yang ketat perlu disetel ulang saat development karena Vite menginjeksi script untuk hot reload.
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'Mulai dari kebijakan ketat, lalu longgarkan hanya yang dibutuhkan aplikasi. Inline script dan eval sebaiknya dihindari di production.
React me-render teks dengan escaping otomatis — teks dari pengguna tidak akan dieksekusi sebagai HTML. Bahaya muncul saat kalian sengaja menggunakan HTML mentah lewat dangerouslySetInnerHTML. Jika wajib, pastikan konten disanitasi di server dengan library seperti sanitize-html.
import sanitizeHtml from "sanitize-html";
export async function action({ request }) {
const formData = await request.formData();
const konten = sanitizeHtml(String(formData.get("konten")));
await simpan(konten);
return redirect("/posting");
}Sanitasi terjadi di server sebelum data disimpan, bukan di client. Rule: bersihkan input di titik masuk, bukan di titik keluar.
Remix memakai cookie dengan sameSite: "lax" di episode 11, yang memblokir sebagian besar serangan CSRF. Lapisan tambahan: validasi header Origin pada action yang sensitif. Periksa bahwa request berasal dari domain aplikasi kalian sebelum memproses perubahan data.
Injection terjadi saat input pengguna dirangkai langsung ke dalam query. ORM seperti Prisma memakai parameterized query secara internal, jadi aman — selama kalian tidak menulis SQL mentah dengan string concatenation. Jangan pernah menggabungkan input pengguna langsung ke query SQL.
Setiap input yang masuk harus divalidasi di server: panjang, tipe, dan format. Library seperti Zod memberi schema yang bisa dipakai ulang antara validasi dan typing. Schema di-parse terhadap data yang masuk; hasilnya aman untuk dipakai.
import { z } from "zod";
const schemaPost = z.object({
judul: z.string().min(3).max(100),
konten: z.string().min(10),
});
export async function action({ request }) {
const formData = await request.formData();
const hasil = schemaPost.safeParse(Object.fromEntries(formData));
if (!hasil.success) {
return { errors: hasil.error.flatten().fieldErrors };
}
await simpan(hasil.data);
return redirect("/posting");
}safeParse tidak melempar error; hasil.success menandakan valid atau tidak. Data yang sudah tervalidasi aman untuk disimpan ke database.
Selain format, batasi ukuran input dan tipe field. Upload file punya batas ukuran sendiri — jangan biarkan satu request menelan memori server. Rate limiting dan ukuran maksimum request adalah pertahanan infrastruktur yang penting.
Semua secret — kunci API, database url, session secret — harus tinggal di environment variables server. Jangan pernah menuliskannya di kode atau mengirimnya ke client. Remix tidak pernah mengirim kode server ke browser; pastikan secret hanya dibaca di loader dan action.
Pisahkan secret per environment: development, staging, dan production memakai nilai berbeda. Lakukan rotasi secret secara berkala, terutama jika ada dugaan kebocoran. Catat di mana setiap secret dipakai agar rotasi tidak membahayakan layanan lain.
Episode 12 membangun pertahanan berlapis: secure headers dan CSP, pencegahan XSS dengan escaping dan sanitasi, pertahanan CSRF lewat sameSite dan origin check, parameterized query untuk injection, validasi input dengan Zod, serta pengelolaan secret yang ketat. Keamanan bukan fitur, melainkan kebiasaan.
Inti yang harus dibawa pulang:
Di episode 13 selanjutnya kita akan membahas API integration dan external data — mengambil data dari API eksternal di loader dan action, bekerja dengan backend REST dan GraphQL, penanganan auth token dengan aman, serta rate limiting dan fallback error. Keamanan internal sudah dijaga; sekarang saatnya berkomunikasi dengan dunia luar.