Kami menggunakan cookies untuk meningkatkan pengalaman Anda di website ini. Dengan melanjutkan, Anda menyetujui penggunaan cookies sesuai Kebijakan Privasi kami.
Setiap developer bertanggung jawab atas keamanan kode yang mereka tulis. Kenali 10 celah keamanan web paling berbahaya versi OWASP dan cara konkret mencegahnya di aplikasi Anda.
Muhamad Putra Aulia Hidayat
Keamanan Web: Tanggung Jawab Setiap Developer
Breach data bukan hanya masalah perusahaan besar. UMKM dan startup juga sering jadi target karena keamanannya lemah. Ini panduan praktis berdasarkan OWASP Top 10.
Penyebab terbesar data breach. User bisa akses data yang bukan miliknya.
// BURUK - tidak ada cek kepemilikan
export async function GET(req: Request, { params }: { params: { id: string } }) {
const { id } = await params
const order = await db.query("SELECT * FROM orders WHERE id = $1", [id])
return Response.json(order) // Siapa saja bisa akses order orang lain!
}
// BENAR - verifikasi kepemilikan
export async function GET(req: Request, { params }: { params: { id: string } }) {
const { id } = await params
const user = await getAuthUser(req)
if (!user) return Response.json({ error: "Unauthorized" }, { status: 401 })
const order = await db.query(
"SELECT * FROM orders WHERE id = $1 AND user_id = $2",
[id, user.id] // Paksa filter berdasarkan user
)
if (!order) return Response.json({ error: "Not found" }, { status: 404 })
return Response.json(order)
}
Di Supabase, pakai Row Level Security:
CREATE POLICY "Users see own orders"
ON orders FOR SELECT
USING (user_id = auth.uid());
// BURUK
const query = `SELECT * FROM users WHERE email = '${email}'`
// BENAR
const user = await db.query("SELECT * FROM users WHERE email = $1", [email])
// BURUK
function Comment({ content }: { content: string }) {
return <div dangerouslySetInnerHTML={{ __html: content }} />
}
// BENAR
import DOMPurify from "dompurify"
function Comment({ content }: { content: string }) {
const clean = DOMPurify.sanitize(content)
return <div dangerouslySetInnerHTML={{ __html: clean }} />
}
// next.config.js
const securityHeaders = [
{ key: "X-Frame-Options", value: "SAMEORIGIN" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" }
]
module.exports = {
async headers() {
return [{ source: "/(.*)", headers: securityHeaders }]
}
}
// middleware.ts
export async function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith("/api/")) {
const ip = request.ip ?? "anonymous"
const key = `ratelimit:${ip}:${Math.floor(Date.now() / 60000)}`
const requests = await redis.incr(key)
if (requests === 1) await redis.expire(key, 60)
if (requests > 60) {
return Response.json(
{ error: "Too many requests" },
{ status: 429, headers: { "Retry-After": "60" } }
)
}
}
}
import { z } from "zod"
const RegisterSchema = z.object({
email: z.string().email("Email tidak valid"),
password: z.string()
.min(8, "Password minimal 8 karakter")
.regex(/[A-Z]/, "Harus mengandung huruf kapital")
.regex(/[0-9]/, "Harus mengandung angka"),
name: z.string().min(2).max(100).trim(),
})
export async function POST(req: Request) {
const body = await req.json()
const result = RegisterSchema.safeParse(body)
if (!result.success) {
return Response.json(
{ errors: result.error.flatten().fieldErrors },
{ status: 400 }
)
}
const { email, password, name } = result.data
// Lanjut proses dengan data yang sudah validated
}
npm audit)Tips teknologi & bisnis mingguan
Bergabung dengan 2,500+ subscriber yang mendapatkan insight teknologi, tutorial development, dan tips bisnis digital langsung ke inbox mereka setiap minggu.
Dapatkan tips & insight teknologi terbaru langsung ke inbox Anda.
© 2026 PT Digital Uptime Teknologi Informasi. Hak cipta dilindungi.