Broken Access Control telah menjadi kerentanan teratas selama tiga siklus OWASP berturut-turut karena prevalensinya yang tinggi dan dampaknya yang besar. Dalam sistem ERP, bentuk paling umum adalah IDOR (Insecure Direct Object Reference), di mana endpoint seperti GET /invoices/:id tidak memverifikasi bahwa pengguna yang meminta memiliki sumber daya tersebut. Solusinya adalah selalu memfilter query database berdasarkan user_id atau tenant_id, dan memusatkan pemeriksaan kepemilikan dalam metode canAccess(user, resource) per tipe sumber daya — bukan menyebarkannya ke seluruh file layanan.
Post ini merekomendasikan lima header yang mencegah seluruh kelas serangan tanpa biaya tambahan: Content-Security-Policy mencegah XSS, X-Frame-Options: DENY mencegah clickjacking, X-Content-Type-Options: nosniff mencegah serangan MIME-sniffing, Strict-Transport-Security menegakkan HTTPS, dan Referrer-Policy: strict-origin-when-cross-origin membatasi kebocoran referrer. Di Next.js, header ini ditambahkan di next.config.js headers(); di nginx melalui direktif add_header di blok server. Konfigurasi dapat diverifikasi di securityheaders.com.
Post ini merekomendasikan access token JWT berumur pendek dengan kedaluwarsa 15 menit yang dipasangkan dengan refresh token 7 hari yang disimpan dalam cookie httpOnly — bukan localStorage — untuk membatasi dampak pencurian token. Endpoint login harus dibatasi pada 5 upaya per menit per IP dengan lockout 15 menit, dan akun dikunci setelah 10 kegagalan login disertai notifikasi email ke pengguna. Password harus di-hash dengan bcrypt pada faktor biaya 12, dan registrasi atau perubahan password baru harus diperiksa melalui API HaveIBeenPwned.
Di salah satu server VPS produksi penulis, penyerang menemukan file .env yang terekspos di direktori publik karena document root nginx dikonfigurasi secara keliru sehingga menyajikan direktori induk, bukan folder publik yang dimaksud. File tersebut berisi kredensial database dan kunci API. Pelanggaran ini terdeteksi melalui log akses sebelum kredensial disalahgunakan, dan semua rahasia segera dirotasi. Pelajaran utamanya adalah memverifikasi document root nginx mengarah tepat ke jalur publik yang dimaksud dan memastikan .env ada di .gitignore menggunakan git ls-files --ignored.
Post ini (A09) merekomendasikan pencatatan semua peristiwa autentikasi beserta alamat IP, semua kegagalan otorisasi, semua tindakan admin, dan semua kegagalan validasi input — serta menyimpan log tersebut di server terpisah atau layanan logging terkelola agar penyerang yang berhasil masuk tidak dapat menghapusnya. Untuk SSRF (A10), jika aplikasi mengambil URL yang disediakan pengguna, validasi URL terhadap allowlist domain yang diizinkan dan blokir permintaan ke rentang IP privat (10.x, 172.16–31.x, 192.168.x); gunakan library validasi URL, bukan regex, karena regex terlalu mudah dilewati dengan karakter unicode atau trik encoding IP.
Daftar OWASP Top 10 ada di mana-mana, tetapi sebagian besar liputan memperlakukannya sebagai kategori abstrak daripada masalah rekayasa konkret. Saya mendekati keamanan secara berbeda setelah berpartisipasi dalam kompetisi Capture the Flag PwC pada 2023, di mana saya melihat langsung bagaimana kerentanan teoretis diterjemahkan ke eksploitasi aktual. Saya menjalankan infrastruktur VPS produksi yang diprobing setiap hari (fail2ban memblokir ratusan upaya per hari), dan saya membangun sistem ERP API-first di mana kegagalan keamanan berarti paparan data keuangan nyata.
Kerentanan #1 untuk tiga siklus OWASP berturut-turut. Broken access control berarti pengguna dapat bertindak di luar izin yang dimaksudkan — mengakses catatan pengguna lain, melakukan tindakan admin, atau melihat data yang seharusnya tidak mereka lihat. Bentuk paling umum yang saya temui dalam sistem ERP adalah IDOR (Insecure Direct Object Reference): endpoint API seperti GET /invoices/:id yang tidak memverifikasi pengguna yang meminta memiliki faktur tersebut.
Pola yang saya gunakan di setiap endpoint NestJS ERP: ekstrak pengguna dari JWT dalam guard, lampirkan pengguna ke request, dalam metode layanan selalu filter berdasarkan user_id (atau tenant_id untuk multi-tenant). Jangan pernah mempercayai ID sumber daya dari request body tanpa memverifikasi kepemilikan. Untuk operasi admin, gunakan guard admin terpisah yang memeriksa peran secara eksplisit.
// NestJS: Ownership check decorator pattern
@Injectable()
export class OwnershipGuard implements CanActivate {
constructor(private readonly invoiceService: InvoiceService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest()
const user = request.user
const resourceId = request.params.id
// Always query with user filter — never trust raw ID
const resource = await this.invoiceService.findOne({
where: { id: resourceId, userId: user.id }
})
if (!resource) {
throw new ForbiddenException('Resource not found or access denied')
}
request.resource = resource
return true
}
}
// Security headers in Next.js (next.config.js)
const securityHeaders = [
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
{
key: 'Content-Security-Policy',
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-inline'", // adjust for your needs
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"connect-src 'self' https://api.yourdomain.com",
].join('; ')
},
{
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload'
},
]Dari pengalaman saya mengamankan sistem ERP: implementasikan layanan otorisasi terpusat daripada menyebar pemeriksaan kepemilikan di seluruh metode layanan. Setiap tipe sumber daya (Invoice, Order, Employee) harus memiliki metode canAccess(user, resource) yang memusatkan logika. Ini membuat audit keamanan menjadi mudah — Anda meninjau logika otorisasi di satu tempat.
Kegagalan kriptografi mencakup enkripsi yang tidak memadai, algoritma yang lemah, dan kegagalan manajemen kunci. Dalam praktik: selalu gunakan bcrypt (faktor biaya 12+) untuk hashing password, jangan pernah MD5 atau SHA1. Gunakan TLS 1.2+ di mana-mana. Simpan rahasia dalam variabel lingkungan atau manajer rahasia, jangan pernah dalam kode atau file konfigurasi yang di-commit.
HTTP security header gratis dan membutuhkan 10 menit untuk dikonfigurasi tetapi mencegah seluruh kelas serangan. Header yang saya konfigurasikan di setiap deployment: Content-Security-Policy (mencegah XSS), X-Frame-Options: DENY (mencegah clickjacking), X-Content-Type-Options: nosniff (mencegah MIME sniffing), Strict-Transport-Security (menegakkan HTTPS), Referrer-Policy: strict-origin-when-cross-origin.
Di salah satu server VPS produksi saya, saya menemukan melalui log fail2ban bahwa penyerang telah menemukan file .env yang terekspos di direktori publik. Konfigurasi Nginx memiliki document root yang tidak diatur dengan benar yang menyajikan direktori induk. File .env berisi kredensial database dan kunci API. Untungnya, saya menangkapnya melalui log akses sebelum kredensial disalahgunakan. Segera rotasi semua rahasia.
Miskonfigurasi keamanan adalah temuan paling umum dalam deployment nyata. Dalam daftar periksa pengerasan VPS saya: nonaktifkan login SSH root, gunakan autentikasi kunci SSH saja (PasswordAuthentication no di sshd_config), jalankan fail2ban dengan aturan SSH yang agresif (3 kegagalan = ban 1 jam), nonaktifkan port yang tidak digunakan, hapus blok server nginx default yang mengekspos versi server.
# VPS hardening checklist (Ubuntu 22.04)
# 1. Disable root SSH login
sudo sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl reload sshd
# 2. Install and configure fail2ban
sudo apt install -y fail2ban
sudo tee /etc/fail2ban/jail.local > /dev/null <<'EOF'
[sshd]
enabled = true
port = ssh
maxretry = 3
bantime = 3600
findtime = 600
[nginx-limit-req]
enabled = true
port = http,https
maxretry = 20
bantime = 86400
EOF
sudo systemctl enable --now fail2ban
# 3. UFW firewall
sudo ufw default deny incoming
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
# 4. Audit with lynis
sudo apt install -y lynis
sudo lynis audit system
# 5. Check for exposed .env files
curl -s https://yourdomain.com/.env | head -3
# Should return 403 or 404, never file contentsKegagalan autentikasi mencakup kata sandi lemah, MFA yang hilang, manajemen sesi yang rusak, dan kerentanan credential stuffing. Yang saya implementasikan: rate limiting pada endpoint login (5 upaya per menit per IP, kemudian lockout 15 menit), bcrypt dengan biaya 12 untuk semua kata sandi, JWT dengan kedaluwarsa access token 15 menit dan refresh token 7 hari disimpan dalam cookie httpOnly.
A09 Kegagalan Logging dan Pemantauan Keamanan: Anda membutuhkan log untuk mendeteksi pelanggaran. Log semua peristiwa autentikasi (sukses dan gagal dengan IP), semua kegagalan otorisasi, semua tindakan admin, dan semua kegagalan validasi input. A10 SSRF: jika aplikasi Anda mengambil URL yang disediakan pengguna, validasi URL terhadap allowlist domain yang diizinkan dan blokir permintaan ke rentang IP privat.
Sumber & Bacaan Lanjutan