Pendekatan defense-in-depth di Next.js App Router
Untuk menahan berbagai vektor serangan, Next.js App Router harus membangun lapisan pelindung: validasi session, handling secret, input sanitasi, kontrol upload, dan pembatasan rate. Pada bagian ini kita langsung membahas bagaimana setiap lapisan bekerja, mengapa penting, dan bagaimana diterapkan di lingkungan produksi.
1. Menetapkan landasan session dan secret
Session modern di App Router bergantung pada secret yang tersembunyi (misalnya NEXTAUTH_SECRET atau SECRET_KEY) dan token yang di-serialize dalam cookie atau header. Pastikan secret tidak disimpan di repo publik: gunakan .env dengan akses terbatas dan mekanisme rotasi. Pada server, validasi dilakukan terhadap signature dan expiry token. Jangan hanya mengandalkan NextAuth default; tambahkan pemeriksaan eksplisit:
import { getToken } from 'next-auth/jwt';
export async function getSession(req) {
const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });
if (!token || token.exp < Date.now() / 1000) {
return null;
}
if (token.role !== 'user') {
throw new Error('Role tidak sesuai');
}
return token;
}Gunakan fungsi serupa di API route maupun middleware untuk memastikan akses konsisten. Simpan secret hanya dalam environment dengan scope minimal, dan catat jika terjadi rotate dengan script deployment yang memuat secret baru.
2. Validasi input dan upload aman
Input pelanggan—termasuk file upload—adalah titik masuk serangan injeksi. Dalam App Router, lakukan:
- Validasi schema menggunakan zod atau yup di handler (misalnya
route.ts): pastikan tipe, panjang, dan format sesuai. - Proteksi upload dengan memindahkan file ke storage trusted melalui API internal dan menghindari langsung memuat file ke filesystem publik.
- Sanitasi metadata untuk mencegah path traversal.
Contoh validasi singkat:
import { z } from 'zod';
const uploadSchema = z.object({
fileName: z.string().min(3).max(100),
contentType: z.enum(['image/png', 'image/jpeg']),
});
export async function POST(req: Request) {
const data = await req.json();
const parsed = uploadSchema.safeParse(data);
if (!parsed.success) {
return new Response('Payload tidak valid', { status: 422 });
}
// lanjutkan proses upload ke storage
}Jika menerima binary, periksa header content-type dan ukuran sebelum menulis ke backend storage; jangan percaya client.
3. Rate limit dan abuse prevention
Agregasi abuse sering terjadi pada endpoint publik. Terapkan rate limiting berbasis IP/identitas dengan mekanisme store di Redis, Deno KV, atau edge cache. Pendekatan umum:
- Gunakan bucket token atau counter sliding window.
- Reset counter dengan batas waktu pendek (misalnya 1 menit).
- Catat request yang diblokir untuk analisis.
Contoh konsep sederhana di middleware:
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
const MAX_REQUESTS = 30;
const WINDOW_MS = 60 * 1000;
const rateStore = new Map();
export function middleware(req: NextRequest) {
const key = req.ip ?? 'unknown';
const record = rateStore.get(key) ?? { count: 0, reset: Date.now() + WINDOW_MS };
if (Date.now() > record.reset) {
record.count = 0;
record.reset = Date.now() + WINDOW_MS;
}
record.count += 1;
rateStore.set(key, record);
if (record.count > MAX_REQUESTS) {
return NextResponse.json({ error: 'Rate limit tercapai' }, { status: 429 });
}
return NextResponse.next();
} Untuk production, ganti Map dengan store terdistribusi (Redis, Cloudflare KV, dsb.) dan pertimbangkan header Retry-After. Jangan lupa mencatat user agent dan timestamp agar bisa meninjau pola abuse.
4. Middleware dan edge logic untuk segala lapisan
Gunakan middleware untuk pengecekan awal: sesuaikan middleware.ts di App Router agar memverifikasi session, secret custom, dan rate limit sebelum request masuk route.
import { NextRequest, NextResponse } from 'next/server';
import { verify } from 'jsonwebtoken';
export async function middleware(req: NextRequest) {
const token = req.cookies.get('session-token');
if (!token) {
return NextResponse.redirect(new URL('/auth/signin', req.url));
}
try {
verify(token, process.env.SESSION_SECRET ?? '');
} catch (error) {
return NextResponse.json({ error: 'Session invalid' }, { status: 401 });
}
return NextResponse.next();
}
Letakkan middleware di /middleware.ts dan gunakan matcher untuk membatasi cakupan. Pastikan fungsi middleware bersifat idempotent dan tidak menyimpan state memori di edge runtimes.
5. Langkah pengujian keamanan
Untuk memverifikasi defense-in-depth:
- Jalankan unit test untuk validasi schema dan handler error parallel.
- Gunakan tools seperti OWASP ZAP atau curl untuk mencoba request tanpa session, dengan expired token, dan dengan payload oversize.
- Simulasikan rate limit dengan
abatauwrkuntuk memastikannya berfungsi dan mengembalikan 429. - Periksa secret rotation dengan deployment dummy dan cek service masih bisa membaca environment baru.
Debugging tip: log request ID dan timestamp sebelum respon ditolak. Pantau log rate limit agar tidak menolak pengguna sah secara tidak sengaja.
Kesimpulan
Defense-in-depth di Next.js berarti menggabungkan validasi session/secret, sanitasi upload, rate limiting, dan middleware edge agar tidak ada celah tunggal yang bisa dimanfaatkan. Menerapkan pendekatan ini secara konsisten membantu menjaga integritas aplikasi tanpa mengorbankan pengalaman pengguna.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!