Belajar Backend Developer - Web Security Lanjutan
Episode 18 of 28

Belajar Backend Developer - Web Security Lanjutan

Membedah tiga serangan paling berbahaya di API: SQL injection, IDOR yang membocorkan data milik user lain, dan SSRF yang menjadikan server sebagai pelompat, lengkap dengan cara menemukan dan menambalnya

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

Episode 17 membangun pertahanan dasar. Sekarang kita membedah tiga serangan paling berbahaya yang menyerang logika API itu sendiri — bukan sekadar konfigurasi: SQL injection, IDOR, dan SSRF. Ketiganya termasuk kelas "broken access control" dan "injection" di OWASP Top 10, dan semuanya bisa ditemukan lewat review kode yang sistematis.

Mengapa harus paham tiga serangan ini secara mendalam? Karena semuanya bisa dicegah di level kode, dan semuanya rutin ditemukan di aplikasi produksi nyata — termasuk di perusahaan besar. Security review yang baik bukan menebak-nebak, melainkan mencari pola spesifik. Episode ini mengajarkan pola itu dan cara menambalnya.

SQL Injection

Kita sudah menyentuhnya di episode 17 — sekarang kita bedah mengapa ia begitu merusak dan bentuk-bentuknya.

Blind SQL Injection

Terkadang output tidak tampil di response, tapi bisa disimpulkan dari perbedaan perilaku. Contoh: payload ' AND 1=1-- selalu berhasil, ' AND 1=2-- selalu gagal. Penyerang bisa menebak karakter demi karakter isi database — disebut blind boolean-based injection:

text
/users?q=' OR (SELECT substring(password,1,1) FROM users LIMIT 1)='a'-- 
/users?q=' OR (SELECT substring(password,1,1) FROM users LIMIT 1)='b'--

Setiap percobaan memberi jawaban benar/salah — dan dengan 62 karakter x panjang password, seluruh hash bisa ditebak otomatis.

Mitigasi

Parameterized query menutup semua bentuk injection
const { rows } = await db.query(
  `SELECT * FROM users WHERE email = $1 AND password_hash = $2`,
  [email, hash],
)

Whitelist untuk struktur — untuk ORDER BY/LIMIT yang tidak bisa parameterized, jangan masukkan input mentah:

Whitelist kolom untuk ORDER BY
const ALLOWED_SORT = new Set(["price", "created_at", "name"])
 
function buildOrderBy(input: string) {
  if (!ALLOWED_SORT.has(input)) return "created_at"
  return input
}

IDOR: Insecure Direct Object Reference

IDOR terjadi saat aplikasi memakai identifier dari user tanpa memeriksa kepemilikan. Lihat contoh rentan:

RENTAN - IDOR
router.get("/v1/orders/:id", requireAuth, async (req, res) => {
  const { rows } = await db.query(
    "SELECT * FROM orders WHERE id = $1",
    [req.params.id],
  )
  res.json({ data: rows[0] })
})

User 5 bisa membaca order milik user lain hanya dengan mengganti id di URL. Validasi kepemilikan hilang — ini access control failure, bukan sekadar bug.

Mitigasi: Cek Kepemilikan

Aman - periksa kepemilikan di query
router.get("/v1/orders/:id", requireAuth, async (req, res) => {
  const { rows } = await db.query(
    `SELECT * FROM orders
     WHERE id = $1 AND user_id = $2`,
    [req.params.id, req.user.userId],
  )
  if (rows.length === 0) {
    return res.status(404).json({ error: "NOT_FOUND", message: "Order tidak ditemukan" })
  }
  res.json({ data: rows[0] })
})

Dua hal penting:

  • Kepemilikan diperiksa di query yang sama — bukan cek terpisah setelah data diambil (race window).
  • Resource yang bukan milik user dikembalikan 404, bukan 403 — dengan 404, penyerang tidak bisa membedakan "tidak ada" dari "bukan milik saya" (information disclosure).

Note

Pola IDOR sangat umum di API dengan nesting: /users/5/orders/87. Setiap kali ada id dari client, tanyakan: apakah pengguna ini berhak atas resource ini? Jawaban harus di-enforce di query, bukan di UI yang menyembunyikan tombol. Frontend yang menyembunyikan tombol bukan otorisasi (episode 7).

SSRF: Server-Side Request Forgery

SSRF terjadi saat server memanggil URL yang dikendalikan user — dan serangan klasiknya: server dijadikan pelompat ke resource internal.

Contoh fitur yang rawan: "cek apakah URL gambar valid", "ambil preview link", atau webhook yang menerima URL tujuan.

RENTAN - SSRF
router.post("/v1/tools/fetch-preview", async (req, res) => {
  const { url } = req.body
  const response = await fetch(url) // user mengontrol URL!
  res.json({ title: extractTitle(await response.text()) })
})

Penyerang mengirim http://169.254.169.254/latest/meta-data/metadata service cloud yang hanya bisa diakses dari dalam server. Dalam satu request, metadata server (termasuk IAM credentials) bocor.

Mitigasi: Allowlist dan Netralisasi Redirect

Allowlist domain untuk SSRF
const ALLOWED_HOSTS = new Set([
  "img.shop.example",
  "cdn.shop.example",
])
 
router.post("/v1/tools/fetch-preview", async (req, res) => {
  const { url } = req.body
 
  let parsed: URL
  try {
    parsed = new URL(url)
  } catch {
    return res.status(400).json({ error: "INVALID_URL" })
  }
 
  // 1. Allowlist host
  if (!ALLOWED_HOSTS.has(parsed.hostname)) {
    return res.status(400).json({ error: "FORBIDDEN_URL" })
  }
  // 2. Tolak IP internal & metadata
  if (isPrivateIp(parsed.hostname)) {
    return res.status(400).json({ error: "FORBIDDEN_URL" })
  }
 
  const response = await fetch(url, { redirect: "manual" })
  res.json({ title: extractTitle(await response.text()) })
})

Lapisan mitigasi SSRF:

  1. Allowlist domain yang sah — cara paling kuat.
  2. Tolak IP privat/loopback dan metadata service (169.254.169.254).
  3. Jangan ikuti redirect otomatis — redirect bisa membawa ke host yang tidak diizinkan.
  4. Validasi URL dengan parser ketat; jangan cari-cari string.

Security Review: Cara Menemukannya

Buat pola review yang sistematis — bukan baca kode sekilas:

1. Lacak Setiap Input dari Client

Untuk setiap handler, tanyakan: dari mana nilai di req.body, req.params, req.query, dan header? Apa jalurnya sampai ke query/fetch?

2. Cari Pola-Pola Ini

text
- db.query("... " + variabel)        → SQL injection
- SELECT * WHERE id = $1 saja        → IDOR (tanpa user_id)
- fetch(variabelFromBody)            → SSRF
- JSON.stringify(req.body) langsung  → no validation

3. Uji Manual Seperti Penyerang

Uji IDOR sebagai dua user
# sebagai user 5
TOKEN=$(curl -s -X POST /v1/auth/login -d '{"email":"alice@x.com","password":"x"}' | jq -r .data.token)
curl -s http://localhost:3000/v1/orders/87 -H "Authorization: Bearer $TOKEN"
# coba ganti id → apakah masih 200 untuk order milik orang lain?

4. Otomatiskan di Test

Tulis test yang sengaja menyerang: ambil resource milik user lain, kirim payload injection, dan minta fetch ke URL internal. Test ini melindungi dari regresi (episode 8).

Tip

Jadikan security review sebagai checklist di pull request (episode 15): adakah input baru? Adakah query baru? Adakah fetch ke URL? Dengan checklist, tiga serangan ini ketahuan di review — bukan di produksi. Tools bantu: SAST (semgrep, CodeQL) untuk pola otomatis, dan scanner OWASP ZAP untuk uji manual.

Common Pitfalls

404 vs 403 untuk Resource Orang Lain

Selalu 404 saat resource tidak bisa diakses — memberi tahu "ada" lewat 403 adalah bocoran informasi.

Cek Kepemilikan Terpisah dari Query

Cek rows.length dulu lalu query lagi = race window dan query kedua bisa lupa filter. Satu query dengan filter kepemilikan.

fetch Mengikuti Redirect

Redirect bisa menembus allowlist host. Set redirect: "manual" dan validasi ulang lokasi baru.

Whitelist vs Blacklist

Blacklist ("tolak localhost") selalu bisa dilewati (127.0.0.1, 0177.0.0.1, DNS rebinding). Allowlist ("hanya domain ini") jauh lebih kuat.

Warning

Jangan pernah "memperbaiki" injection dengan escape string manual — selalu ada celah. Satu-satunya mitigasi yang benar untuk SQL injection adalah parameterized query, dan untuk SSRF adalah allowlist + tidak mengikuti redirect. Solusi kreatif manual adalah cara tercepat menambah CVE baru.

Penutup

Episode 18 membedah tiga serangan paling berbahaya: SQL injection (termasuk blind) yang ditutup dengan parameterized query, IDOR yang memerlukan cek kepemilikan di query, dan SSRF yang ditambal dengan allowlist host dan anti-redirect.

Inti yang harus dibawa pulang:

  • SQL injection: parameterized query selalu; whitelist untuk struktur query.
  • IDOR: cek user_id di query yang sama; balas 404 untuk resource tak berhak.
  • SSRF: allowlist host, tolak IP internal, jangan ikuti redirect.
  • Security review sistematis: lacak input → cari pola → uji manual → otomatiskan.
  • Whitelist selalu menang atas blacklist.

Di episode 19 selanjutnya kita akan melindungi data pengguna: privacy & compliance data — GDPR, enkripsi saat transit dan saat istirahat, serta audit. Sampai jumpa di episode 19!

Belajar Backend Developer - Web Security Lanjutan | Belajar Backend