Memaksimalkan fitur keamanan bawaan framework: CSRF protection, ORM parameterized queries, template auto-escaping, security headers middleware, dan konfigurasi auth framework populer; plus daftar flag yang jangan pernah kalian matikan beserta alasan teknisnya

Setelah di episode 15 kita mengamankan workload cloud-native, episode ini kembali ke kode dengan lensa yang sering terlupa: framework yang sudah dipakai tim kalian kemungkinan besar sudah menyelesaikan separuh pekerjaan security — dan separuh sisanya rusak oleh orang yang mematikan fiturnya. Django punya CSRF protection dan ORM binding sejak hari pertama; React meng-escape output otomatis; Spring Security menyertakan header hardening siap pakai.
Nilai AppSec engineer di sini unik: kalian bukan yang menulis proteksi — kalian yang memastikan proteksi bawaan aktif, benar konfigurasinya, dan tidak dimatikan. Ini leverage tertinggi karena satu baris config melindungi seluruh codebase.
Peta fitur security pada framework populer:
| Proteksi | Django | Spring | Rails | Laravel | Next.js/Express |
|---|---|---|---|---|---|
| SQLi (ORM binding) | ORM | JPA/Hibernate | ActiveRecord | Eloquent | Prisma/TypeORM |
| XSS (auto-escape) | Template auto | Thymeleaf | ERB | Blade | JSX escaping |
| CSRF | Middleware on | CSRF tokens | protect_from_forgery | Middleware on | Perlu library |
| AuthN/AuthZ | Auth + permissions | Spring Security | Devise | Breeze/Sanctum | NextAuth dsb. |
| Security headers | Middleware | Security config | Secure headers | Middleware | helmet |
Kesimpulan strategisnya: sebelum menulis proteksi manual atau membeli tool baru, audit dulu apa yang sudah tersedia dan apakah aktif. Sering kali temuan "aplikasi rentan CSRF" berakhir menjadi "middleware ada tapi di-skip untuk route baru".
ORM modern membuat query parameterized sebagai perilaku default — inilah alasan SQL injection hampir hilang dari aplikasi framework-native. Bahaya muncul tepat di lubang escape hatch-nya:
// AMAN: parameterized secara internal
const user = await prisma.user.findFirst({
where: { email: userInput },
});
// LUBANG: raw dengan interpolasi string
const user = await prisma.$queryRawUnsafe(
`SELECT * FROM users WHERE email = '${userInput}'`
);
// AMAN jika memang butuh raw: tagged template
const user = await prisma.$queryRaw`SELECT * FROM users WHERE email = ${userInput}`;Pola audit cepat: grep semua panggilan $queryRawUnsafe, sequelize.query, raw(, .fromSql(, dan semacamnya — setiap hit adalah kandidat review. Rule Semgrep kustom dari episode 7 sangat efektif di sini karena polanya spesifik per ORM.
Template engine dan JSX meng-escape secara default, tapi semua punya mekanisme bypass eksplisit — dan itu selalu jadi titik audit pertama saya saat review aplikasi baru:
React : dangerouslySetInnerHTML
Vue : v-html
Django : |safe filter, mark_safe()
Rails : raw(), html_safe
Laravel : {!! $var !!}
Thymeleaf : th:utext (bukan th:text)Setiap penggunaannya harus punya justifikasi + sanitasi upstream (DOMPurify untuk HTML user-generated). Grep keenam pola ini sekali, hasilkan laporan, dan kalian sudah memiliki backlog XSS review yang konkret untuk tim.
Important
Aturan sederhana yang bisa kalian sosialisasikan ke developer: kalau harus memakai escape hatch, nama fungsinya sendiri sudah berteriak bahaya (dangerouslySetInnerHTML, {!! !!}) — perlakukan tiap pemakaian seperti menulis kode SQL mentah: sadar risiko, sanitasi eksplisit.
CSRF token bawaan framework bekerja dengan baik — sampai developer mengalaminya sebagai friction ("form test gagal!") lalu menonaktifkannya. Konfigurasi yang benar:
import { doubleCsrf } from "csrf-csrf";
const { generateToken, validateRequest } = doubleCsrf({
getSecret: () => process.env.CSRF_SECRET,
cookieName: "__Host-psl.x-csrf-token",
});
app.use((req, res, next) => { req.csrfToken = generateToken(req, res); next(); });Catatan ekosistem: pakai csrf-csrf atau mekanisme SameSite + custom header; csurf lama sudah deprecated.
Header security adalah proteksi termurah yang sering absen. Di Express, helmet satu baris; di framework lain setara middleware bawaan:
import helmet from "helmet";
app.use(
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"], // tanpa 'unsafe-inline'
objectSrc: ["'none'"],
frameAncestors: ["'none'"],
},
},
frameguard: { action: "deny" },
referrerPolicy: { policy: "strict-origin-when-cross-origin" },
})
);CSP yang ketat adalah net kedua untuk XSS: meski satu payload lolos encoding, ia tak bisa memuat script eksternal. Mulai dari mode report-only (Content-Security-Policy-Report-Only) agar tidak merusak frontend, pantau pelanggaran dua minggu, baru enforce.
Aturan keputusan yang saya pegang:
hashPassword() yang memanggil Argon2 dengan parameter terstandar, sehingga tidak ada developer yang mengatur sendiri.Tip
Jadikan audit "konfigurasi security framework" bagian dari onboarding AppSec ke sebuah service baru: cek lima hal — CSRF aktif, ORM tanpa raw unsafe, escape hatch terinventaris, header security on, auth flow pakai modul resmi. Satu jam kerja yang menemukan mayoritas masalah sistemik.
Access-Control-Allow-Origin: * bersama credentials adalah kombinasi yang harus langsung ditolak saat review.Inti yang harus dibawa pulang:
Di episode 17 kita keluar dari perimeter internal: Bug Bounty Coordination — bagaimana organisasi menjalankan program bug bounty, cara men-triage laporan researcher, dan seni coordinated disclosure yang menjaga hubungan baik tanpa kehilangan kendali rilis patch. Sampai jumpa di episode 17!