Mengamankan backend berlapis: memahami OWASP Top 10 yang relevan untuk API, menerapkan input validation dan rate limiting sebagai pertahanan dasar, serta mengelola secrets dengan benar agar kredensial tidak pernah bocor

Di episode 7 kalian membangun autentikasi, dan di episode 0-16 banyak kredensial melintas — database, registry, cloud, dan email. Sekarang saatnya melihat keamanan sebagai sistem menyeluruh, bukan sekumpulan trik. Attack surface backend sangat luas: input dari user, koneksi ke database, integrasi pihak ketiga, dan konfigurasi cloud.
Mengapa security penting bagi backend developer? Karena backend adalah tempat data berharga dan pintu masuk sistem. 90% serangan yang tercatat menyerang aplikasi web/API, dan sebagian besar bisa dicegah dengan disiplin dasar. Episode ini membangun lapisan pertahanan inti: OWASP Top 10, input validation, rate limiting, dan pengelolaan secrets.
OWASP Top 10 adalah daftar risiko keamanan aplikasi web paling kritis yang diperbarui berkala. Yang paling sering menyerang API backend:
| Peringkat | Risiko | Kaitannya dengan backend |
|---|---|---|
| A01 | Broken Access Control | Otorisasi lemah (IDOR — episode 18) |
| A02 | Cryptographic Failures | Secrets bocor, enkripsi salah (episode 19) |
| A03 | Injection | SQL injection (episode 18) |
| A04 | Insecure Design | Logika bisnis lemah |
| A05 | Security Misconfiguration | Header salah, debug terbuka, CORS longgar |
| A06 | Vulnerable Components | Dependency tidak di-update |
| A07 | Auth Failures | Sesuai episode 7 |
| A08 | Software/Data Integrity | Supply chain, dependency |
| A09 | Logging Failures | Sesuai episode 11 |
| A10 | SSRF | Server memanggil URL dari user (episode 18) |
Episode 17-18 membahas poin-poin ini secara praktis.
Aturan emas backend: jangan pernah percaya input dari client. Setiap field yang masuk harus divalidasi — tipe, format, rentang, dan panjang. Tanpa validasi, input menjadi pintu masuk injection dan logika rusak.
import { z } from "zod"
const createProductSchema = z.object({
name: z.string().min(1).max(255),
price: z.number().positive(),
stock: z.number().int().nonnegative(),
category: z.enum(["electronics", "accessories", "books"]),
})
const createOrderSchema = z.object({
items: z.array(z.object({
productId: z.number().int().positive(),
qty: z.number().int().min(1).max(100),
})).min(1),
})
router.post("/v1/products", async (req, res, next) => {
const parsed = createProductSchema.safeParse(req.body)
if (!parsed.success) {
return res.status(400).json({
error: "VALIDATION_FAILED",
message: "Body tidak valid",
details: parsed.error.issues,
})
}
const product = await createProduct(parsed.data)
res.status(201).json({ data: product })
})Prinsip validasi:
req.body langsung) di query.Note
Validasi bukan hanya soal injection — data yang salah bentuk (harga negatif, qty nol) merusak logika bisnis diam-diam. Schema validation seperti Zod menangkap keduanya sekaligus. Ini juga alasan framework seperti FastAPI menyediakan validasi bawaan (Pydantic) — pakai itu, jangan tulis manual.
Salah satu serangan tertua yang masih membunuh sampai sekarang: SQL injection — memasukkan kode SQL lewat input. Contoh yang rentan:
const { rows } = await db.query(
`SELECT * FROM products WHERE name = '${req.query.q}'`,
)Jika q berisi ' OR '1'='1, query menjadi WHERE name = '' OR '1'='1' — semua produk terbaca. Perbaikan: parameterized query — nilai dikirim terpisah dari query, sehingga tidak pernah dieksekusi sebagai SQL.
const { rows } = await db.query(
`SELECT * FROM products WHERE name = $1`,
[req.query.q],
)$1 adalah placeholder; driver mengirim nilainya sebagai data, bukan sebagai SQL. Aturan: semua query dengan input user wajib parameterized — tanpa kecuali, termasuk LIKE, IN, dan ORDER BY (yang terakhir divalidasi dengan whitelist kolom, bukan dimasukkan mentah).
Banyak serangan dicegah cukup dengan header HTTP yang benar:
res.set("X-Content-Type-Options", "nosniff")
res.set("X-Frame-Options", "DENY")
res.set("Referrer-Policy", "no-referrer")
res.set("Content-Security-Policy", "default-src 'none'")Di Express, pakai library helmet yang menyetel semua header ini sekaligus:
import helmet from "helmet"
app.use(helmet())Konfigurasi lain yang wajib:
* untuk API berauth.Rate limiting membatasi berapa banyak request dalam satu jendela waktu — pertahanan terhadap brute-force dan DoS. Di episode 20 kita bahas lengkap; intinya di sini:
import { rateLimit } from "express-rate-limit"
const limiter = rateLimit({
windowMs: 60 * 1000,
limit: 100,
standardHeaders: "draft-7",
legacyHeaders: false,
message: { error: "TOO_MANY_REQUESTS", message: "Terlalu banyak request" },
})
app.use(limiter)Tanpa rate limit, endpoint login bisa dicoba jutaan kali, dan satu script bisa membanjiri server.
Kredensial — DATABASE_URL, JWT_SECRET, AWS_ACCESS_KEY — adalah target utama penyerang. Aturan pengelolaan:
Secrets tidak boleh di source code, tidak di commit, tidak di Dockerfile (episode 14). Selalu lewat environment variable.
const jwtSecret = process.env.JWT_SECRET
if (!jwtSecret) {
throw new Error("JWT_SECRET tidak diatur")
}Environment variable di server masih bisa bocor lewat log/config. Untuk produksi, gunakan Secret Manager (AWS Secrets Manager, GCP Secret Manager, Vault):
aws secretsmanager create-secret --name shop/jwt --secret-string "..."
aws secretsmanager get-secret-value --secret-id shop/jwt --query SecretStringWarning
Secret yang sudah pernah bocor (ter-commit lalu dihapus) tetap berbahaya — git history masih menyimpannya. Segera rotasi secret tersebut, bukan sekadar menghapus dari kode. Cek kebocoran dengan tools seperti gitleaks di pipeline CI (episode 15) agar secret tidak pernah masuk history sejak awal.
Terapkan checklist ini pada API toko online:
Frontend bisa dilewati siapa saja. Validasi harus selalu ada di backend.
Input mentah di query = SQL injection. Parameterized selalu.
* untuk API Ber-AuthAccess-Control-Allow-Origin: * berarti situs mana pun bisa memanggil API dari browser user yang sedang login. Batasi ke origin yang dikenal.
Sekali bocor, selamanya berisiko. Rotasi dan gitleaks di CI.
Episode 17 membangun pertahanan inti: peta OWASP Top 10 untuk API, input validation dengan schema, parameterized query melawan SQL injection, security headers, rate limiting, dan pengelolaan secrets yang disiplin.
Inti yang harus dibawa pulang:
Di episode 18 selanjutnya kita akan mendalami tiga serangan paling berbahaya: web security lanjutan — SQL injection, IDOR, dan SSRF, lengkap dengan cara menemukan dan menambalnya. Sampai jumpa di episode 18!