Security
Proteksi CSRF, CORS, header keamanan, keamanan cookie, dan lainnya.
Bosia menyertakan beberapa fitur keamanan yang diaktifkan secara bawaan.
Proteksi CSRF
Semua request yang tidak aman (POST, PUT, PATCH, DELETE) divalidasi terhadap origin server. Ini menggunakan pendekatan yang sama seperti SvelteKit — memeriksa header Origin atau Referer.
- Method aman (GET, HEAD, OPTIONS) dikecualikan
- Request yang mengubah state tanpa
Origin/Refererditolak dengan kode 403 - Request lintas-origin dari origin yang tidak diharapkan diblokir
Konfigurasi
Izinkan origin tambahan melalui variabel lingkungan CSRF_ALLOWED_ORIGINS:
CSRF_ALLOWED_ORIGINS=https://app.example.com, https://mobile.example.comDeployment di belakang reverse-proxy (`TRUST_PROXY`)
Secara bawaan Bosia tidak mempercayai header X-Forwarded-Host dan X-Forwarded-Proto saat menentukan apakah origin sebuah request cocok. Server yang langsung terekspos ke internet bisa membiarkan klien memalsukan origin yang diharapkan melalui header forwarded buatan penyerang, sehingga melewati pemeriksaan CSRF.
Saat Bosia berjalan di belakang reverse proxy atau load balancer (nginx, Caddy, Cloudflare, ALB, dsb.), host publik biasanya berbeda dengan header Host yang diterima proses Bun di dalamnya. Pada kasus itu, setel:
TRUST_PROXY=trueAktifkan hanya jika semua kondisi berikut terpenuhi:
- Ada proxy/load balancer di depan Bosia.
- Proxy tersebut menghapus header
X-Forwarded-*yang dikirim klien sebelum diteruskan (umumnya proxy melakukan ini secara default; pastikan punyamu juga). - Proxy menambahkan
X-Forwarded-Host/X-Forwarded-Protosendiri yang mencerminkan origin publik.
Jangan menyetel TRUST_PROXY=true jika Bosia langsung terekspos ke internet tanpa proxy, atau jika kamu tidak bisa memastikan proxy membersihkan header forwarded yang masuk — hal itu akan membuka kembali celah pemalsuan yang ditutup oleh nilai default.
Pada mode dev, bun run dev menjalankan proxy di depan app server internal pada port berbeda. Dev proxy menyuntikkan X-Forwarded-Host / X-Forwarded-Proto dan menyetel TRUST_PROXY=true di proses app secara otomatis, sehingga submission form same-origin dan POST bekerja tanpa konfigurasi tambahan.
CORS
CORS dinonaktifkan secara bawaan. Aktifkan dengan menyetel origin yang diizinkan:
CORS_ALLOWED_ORIGINS=https://app.example.com, https://admin.example.comPengaturan CORS tambahan:
CORS_ALLOWED_METHODS=GET, POST, PUT, DELETE
CORS_ALLOWED_HEADERS=Content-Type, Authorization
CORS_EXPOSED_HEADERS=X-Request-Id
CORS_CREDENTIALS=true
CORS_MAX_AGE=86400Request preflight OPTIONS ditangani secara otomatis saat CORS dikonfigurasi. Preflight juga memvalidasi method yang diminta (Access-Control-Request-Method) dan header (Access-Control-Request-Headers) terhadap CORS_ALLOWED_METHODS / CORS_ALLOWED_HEADERS. Preflight yang meminta method atau header di luar daftar izin dijawab dengan 403 (tetap membawa Access-Control-Allow-Origin dan Vary: Origin), sehingga klien yang salah konfigurasi memunculkan pesan "not allowed by CORS policy" yang jelas di devtools browser, bukan diloloskan dengan 204 yang permisif.
Saat CORS dikonfigurasi, setiap response menyertakan Vary: Origin — termasuk response untuk origin yang tidak ada di daftar izin. Ini mencegah cache bersama (CDN, cache HTTP browser) secara tidak sengaja menyajikan response berisi Access-Control-Allow-Origin: A untuk request dari origin B yang berbeda.
Header Keamanan
Bosia menyetel header-header ini pada setiap response:
| Header | Nilai |
|---|---|
X-Content-Type-Options |
nosniff |
X-Frame-Options |
SAMEORIGIN |
Referrer-Policy |
strict-origin-when-cross-origin |
Mematikan `X-Frame-Options`
Setel DISABLE_X_FRAME_OPTIONS=true untuk menghilangkan header X-Frame-Options. Ditujukan untuk aplikasi yang memang sengaja disematkan sebagai iframe oleh origin lain — hub preview/proxy, alat desain, sandbox runner. Header keamanan lain tetap aktif.
DISABLE_X_FRAME_OPTIONS=trueJika kamu mengontrol embedder dan butuh aturan framing yang lebih ketat, lebih baik gunakan frame-ancestors lewat CSP_DIRECTIVES daripada menghapus X-Frame-Options.
Content Security Policy (berbasis nonce)
CSP dimatikan secara bawaan — menyalakannya tanpa direktif yang tepat akan merusak inline script milikmu dan widget pihak ketiga. Aktifkan dengan menyetel env CSP_DIRECTIVES. Placeholder literal {nonce} akan disubstitusi dengan nonce per-request baru (entropi 128-bit, base64) pada setiap response:
CSP_DIRECTIVES="default-src 'self'; script-src 'self' 'nonce-{nonce}'; style-src 'self' 'unsafe-inline'"Setelah CSP_DIRECTIVES diset, dua hal terjadi pada setiap response:
- Header
Content-Security-Policyyang cocok ditambahkan. - Tag
<script>milik framework — hidrasi page-data, bootstrap tema, SSE reload mode dev, fragment head/body dari plugin yang dikirim lewat framework — distempelnonce="…"agar policy tidak memblokirnya.
Tanpa CSP_DIRECTIVES, framework tidak mengirim header maupun atribut nonce (atribut saja hanya membuang byte — browser hanya membandingkan nonce ketika ada header policy yang menyuruhnya).
Nonce yang sama selalu diekspos di request event sehingga kode pengguna bisa memakainya di policy apa pun:
// Di +page.server.ts load() atau di hook:
event.locals.nonce; // → "kJ3p1f...": unik per requestGunakan nonce pada inline script milikmu agar tetap jalan di bawah policy ini:
<script nonce="{data.nonce}">
console.log("hello");
</script>Teruskan nonce ke page lewat fungsi load():
// +page.server.ts
export async function load({ locals }) {
return { nonce: locals.nonce };
}Keamanan Cookie
API cookie menyertakan beberapa proteksi:
- Pencegahan injeksi header — nilai yang mengandung
;,\r, atau\nditolak - Validasi SameSite — hanya
Strict,Lax, atauNoneyang diterima - Encoding otomatis — nilai cookie dienkode secara aman dengan
encodeURIComponent
Setel opsi cookie yang aman:
event.cookies.set("session", token, {
path: "/",
httpOnly: true, // not accessible via JavaScript
secure: true, // HTTPS only
sameSite: "Lax", // protects against CSRF
maxAge: 60 * 60 * 24 * 7, // 7 days
});Proteksi XSS
Data JSON yang disematkan dalam HTML yang dirender server diescap menggunakan serializer yang aman yang:
- Mengescap
<,>,&,',", dan karakter Unicode yang dapat keluar dari tag script - Menangani referensi sirkular dengan baik
Batas Ukuran Body Request
Ukuran body request dibatasi secara bawaan untuk mencegah denial-of-service:
BODY_SIZE_LIMIT=512K # default
BODY_SIZE_LIMIT=1M # 1 megabyte
BODY_SIZE_LIMIT=10M # 10 megabytes
BODY_SIZE_LIMIT=Infinity # no limit (not recommended)Mendukung sufiks K (kilobyte), M (megabyte), dan G (gigabyte).
Proteksi Path Traversal
Penayangan file statis dan halaman prerendered memvalidasi bahwa path file yang telah diselesaikan tetap berada dalam direktori yang diizinkan, mencegah serangan traversal ../.
Pada waktu build, nilai entries() untuk prerender juga divalidasi: .. dan \ tidak pernah diizinkan di segmen mana pun, dan / hanya diizinkan pada segmen catch-all ([...rest]). Build yang mengembalikan nilai tidak aman akan gagal lebih awal dengan pesan error yang jelas, alih-alih diam-diam menulis HTML ke luar direktori output.
Proteksi Open-Redirect
redirect(status, location) menolak URL eksternal secara bawaan. Setel { allowExternal: true } untuk redirect eksternal yang sah (misalnya provider OAuth):
import { redirect } from "bosia";
redirect(303, "https://accounts.example.com/oauth", { allowExternal: true });Meski allowExternal: true, skema berbahaya — javascript:, data:, vbscript: — selalu ditolak. Skema-skema itu tidak pernah menjadi tujuan redirect yang sah dan bisa disalahgunakan untuk menyuntikkan eksekusi script ke dalam rantai redirect.
Penanganan Error di Produksi
Pada produksi (NODE_ENV=production), stack trace dihapus dari response error untuk mencegah kebocoran detail internal ke klien.