Input Validation untuk Keamanan
Validasi input bukan hanya tentang UX — ini adalah garis pertahanan pertama terhadap serangan. Semua input dari luar (form, API, URL, headers) harus divalidasi sebelum diproses.
Prinsip Validasi
- Whitelist over blacklist — Definisikan apa yang BOLEH, bukan apa yang TIDAK BOLEH
- Validate early — Validasi seawal mungkin, sebelum data diproses
- Validate on server — Validasi frontend adalah UX, validasi backend adalah keamanan
- Reject invalid input — Jangan coba memperbaiki input yang salah
Schema Validation
1. Laravel Form Request
// app/Http/Requests/StoreUserRequest.php
class StoreUserRequest extends FormRequest
{
public function rules(): array
{
return [
"name" => ["required", "string", "max:255"],
"email" => ["required", "email", "unique:users"],
"age" => ["required", "integer", "min:13", "max:120"],
"role" => ["required", "in:user,moderator"],
"website" => ["nullable", "url", "max:500"],
];
}
}
2. Zod (TypeScript)
import { z } from "zod";
const UserSchema = z.object({
name: z.string().min(1).max(255),
email: z.string().email(),
age: z.number().int().min(13).max(120),
role: z.enum(["user", "moderator"]),
website: z.string().url().max(500).optional(),
});
// Validasi
const result = UserSchema.safeParse(req.body);
if (!result.success) {
return res.status(422).json({ errors: result.error.issues });
}
const validData = result.data; // Type-safe!
3. Joi (JavaScript)
const Joi = require("joi");
const schema = Joi.object({
name: Joi.string().required().max(255),
email: Joi.string().email().required(),
age: Joi.number().integer().min(13).max(120).required(),
});
const { error, value } = schema.validate(req.body);
Validasi yang Sering Dilupakan
- Array size — Batasi jumlah item dalam array untuk mencegah DoS
- String length — Batasi panjang string untuk mencegah buffer issues
- Nested depth — Batasi kedalaman JSON untuk mencegah stack overflow
- File upload — Validasi tipe, ukuran, dan isi file
- URL parameter — Validasi format ID, slug, dan parameter lainnya
// Batasi body size di Express
app.use(express.json({ limit: "1mb" })); // Maksimal 1MB
// Laravel: batasi array size
"items" => ["required", "array", "max:50"],
"items.*.id" => ["required", "integer"]
Anti-Pattern: Terlalu Percaya pada Frontend
// Frontend validation ≠ keamanan
// Penyerang bisa bypass frontend validation dengan:
// 1. Browser DevTools
// 2. curl/Postman
// 3. Modifying request di proxy (Burp Suite)
// SELALU validasi ulang di backend!
Client + Server Validation
Praktik terbaik adalah menerapkan validasi di kedua sisi: validasi di client (misalnya dengan Zod di TypeScript) untuk UX yang responsif, dan validasi di server (misalnya Laravel Form Request) untuk keamanan. Validasi client bisa dibypass, jadi validasi server adalah yang wajib.