OWASP Top 10 untuk Backend Engineer
OWASP (Open Worldwide Application Security Project) menerbitkan OWASP Top 10, sebuah daftar sepuluh risiko keamanan aplikasi web paling kritis yang disusun dari data serangan dunia nyata. Daftar ini diperbarui secara berkala; versi terbaru adalah OWASP Top 10 2021. Sebagai backend engineer, Anda adalah garis pertahanan terakhir: semua data, logika bisnis, dan API yang Anda tulis adalah yang paling sering dihadapi penyerang. Artikel ini membahas setiap risiko dengan bahasa sederhana, lengkap dengan contoh serangan, contoh kode Java Spring yang aman, dan cara pencegahannya.
Apa itu OWASP Top 10
OWASP Top 10 bukanlah daftar lengkap semua ancaman yang ada — itu adalah titik awal. Daftar ini disusun dari data statistik nyata (bukan teori), lalu diranking berdasarkan dua hal: prevalensi (seberapa sering terjadi) dan dampak (seberapa parah akibatnya). Jika sebuah kerentanan masuk daftar ini, kemungkinan besar banyak aplikasi di dunia mengalaminya.
| Peringkat 2021 | Nama | Kategori |
|---|---|---|
| A01 | Broken Access Control | Kontrol akses |
| A02 | Cryptographic Failures | Kriptografi |
| A03 | Injection | Kualitas & keamanan data |
| A04 | Insecure Design | Kualitas desain |
| A05 | Security Misconfiguration | Konfigurasi |
| A06 | Vulnerable and Outdated Components | Komponen |
| A07 | Identification and Authentication Failures | Identifikasi & autentikasi |
| A08 | Software and Data Integrity Failures | Integritas |
| A09 | Security Logging and Monitoring Failures | Logging & monitoring |
| A10 | Server-Side Request Forgery (SSRF) | Serangan sisi server |
Daftar ini berubah antar versi. A10 SSRF bahkan baru masuk di versi 2021 setelah peneliti menemukan lonjakan serangan terhadap layanan cloud. Karena itu, anggap OWASP Top 10 sebagai "radar" yang harus dicek rutin, bukan patokan mati.
Cara membaca artikel ini
Untuk setiap risiko, kita bahas tiga hal secara konsisten:
- Apa yang terjadi — deskripsi singkat kerentanannya.
- Contoh serangan — skenario nyata yang biasa dieksploitasi.
- Cara mencegah — prinsip + contoh kode Java Spring yang aman.
Banyak risiko saling terkait: A07 (autentikasi lemah) bisa memicu A01 (akses tak diotorisasi). Karena itu, anggaplah setiap bagian sebagai layer dari pertahanan yang sama, bukan checklist yang berdiri sendiri.
1. A01 — Broken Access Control
Broken access control (kontrol akses rusak) terjadi ketika aplikasi gagal memastikan pengguna hanya bisa mengakses sumber daya yang memang miliknya. Autentikasi menjawab "siapa Anda?", sedangkan otorisasi menjawab "apakah Anda boleh melakukan ini?". A01 adalah kegagalan di jawaban yang kedua, dan ini kategori paling umum di OWASP Top 10 2021.
Contoh serangan: IDOR
IDOR (Insecure Direct Object Reference) adalah kasus paling klasik. Aplikasi menerima id dari URL lalu mengambil data tanpa memeriksa kepemilikan:
GET /api/orders/123 → milik Anda, ditampilkan (200 OK)
GET /api/orders/124 → milik orang lain, TETAP ditampilkan (200 OK!) ✗
Penyerang cukup mengganti angka id di URL untuk membaca pesanan orang lain. Masalahnya bukan pada GET-nya, tapi pada server yang mempercayai id dari klien tanpa verifikasi kepemilikan.
Pencegahan
Prinsip dasarnya: deny by default — setiap akses ditolak kecuali secara eksplisit diizinkan. Cek kepemilikan dilakukan di service layer, bukan hanya di controller:
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public Order getOrderForUser(Long orderId, User currentUser) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new NotFoundException("Order tidak ditemukan"));
// Cek kepemilikan: kalau bukan pemiliknya, balas 404 (bukan 403)
// supaya penyerang tidak bisa "menebak" bahwa data itu ada.
if (!order.getCustomer().getEmail().equals(currentUser.getEmail())) {
throw new NotFoundException("Order tidak ditemukan");
}
return order;
}
}
Beberapa aturan praktis:
- Jangan percaya
iddari klien — selalu validasi izin terhadap resource yang diakses. - Balas
404(seolah tidak ada) untuk resource milik orang lain, bukan403, agar tidak membocorkan keberadaan data. - Gunakan Spring Security authorization dengan
@PreAuthorizeuntuk kontrol berbasis peran, dan cek objek-level di service. - Batasi method HTTP: jangan izinkan
DELETE /api/orders/{id}untuk semua orang.
Ingat: broken access control adalah tentang otorisasi, bukan autentikasi. Login sukses tidak otomatis berarti Anda boleh mengakses apa pun.
2. A02 — Cryptographic Failures
Dulu kategori ini bernama "Sensitive Data Exposure". Nama baru ini lebih akurat karena masalahnya biasanya bukan sekadar "data bocor", melainkan data sensitif tidak dilindungi dengan kriptografi yang benar — saat transit maupun saat istirahat. Contoh: kredensial dikirim lewat HTTP biasa, password disimpan sebagai teks polos, atau nomor kartu kredit disimpan tanpa enkripsi.
Contoh serangan
Data sensitif yang dikirim tanpa HTTPS bisa diintip oleh siapa pun di jalur jaringan (serangan man-in-the-middle). Sementara itu, password yang disimpan sebagai teks polos di database akan langsung terbaca jika database pernah bocor — dan database selalu bisa bocor.
Pencegahan
| Area | Apa yang harus dilakukan |
|---|---|
| Saat transit | Wajib HTTPS/TLS di semua komunikasi; tolak request HTTP di production |
| Saat istirahat | Enkripsi data istirahat (misal kartu kredit, data medis) di database |
| Kredensial | Jangan simpan rahasia, API key, atau token di kode, file config, atau repo |
| Password | Hash dengan algoritma yang tepat — lihat password hashing dengan BCrypt |
| Kriptografi | Jangan pernah membuat algoritma kripto sendiri — selalu gunakan implementasi yang sudah teruji |
Sebagai contoh, memaksa HTTPS di Spring Boot cukup dengan konfigurasi Spring Security:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.requiresChannel(channel -> channel.anyRequest().requiresSecure())
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
return http.build();
}
Aturan emas kriptografi: algoritma kripto yang "dibuat sendiri" hampir pasti lemah. Gunakan standar yang sudah diuji komunitas (BCrypt, AES, TLS), dan serahkan detailnya ke library.
3. A03 — Injection
Injection terjadi ketika input dari pengguna dieksekusi sebagai kode atau perintah oleh aplikasi. Bentuknya beragam: SQL injection, NoSQL injection, LDAP injection, hingga OS command injection. Yang paling sering dan paling merusak adalah SQL injection (OWASP A03 Injection).
Contoh serangan SQL injection
Bayangkan login yang menyusun query dengan concatenation string:
// JANGAN PERNAH lakukan ini!
String sql = "SELECT * FROM users WHERE email = '" + email
+ "' AND password = '" + password + "'";
Jika penyerang memasukkan email ' OR '1'='1, query menjadi:
SELECT * FROM users WHERE email = '' OR '1'='1' AND password = 'x'
Ekspresi '1'='1' selalu bernilai benar, sehingga query mengembalikan seluruh baris users — dan penyerang berhasil "login" sebagai baris pertama, biasanya admin.
Pencegahan: parameterized query
Solusi utamanya adalah jangan pernah menyusun query dengan string concatenation. Gunakan parameterized query, PreparedStatement, atau ORM seperti JPA/Hibernate yang otomatis melakukan parameter binding:
// Aman: nilai diikat sebagai parameter, bukan bagian dari SQL
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);
}
JPA/Hibernate (dengan parameter binding) memisahkan struktur SQL dari data, sehingga karakter ' milik penyerang diperlakukan sebagai data biasa, bukan sintaks SQL. Baca lebih dalam di Query, Pagination & SQL Injection.
Prinsip umum untuk semua jenis injection: perlakukan input sebagai data, bukan kode. Jangan menggabungkan string dari pengguna ke SQL, LDAP, shell, maupun query NoSQL. Validasi tipe dan format input juga membantu, tapi parameterization adalah garis pertahanan utamanya.
4. A04 — Insecure Design
Insecure design berbeda dari kerentanan kode: ini adalah kesalahan di tingkat desain atau arsitektur, sebelum sebaris kode pun ditulis. Aplikasi bisa saja diimplementasikan "dengan benar" tapi tetap berbahaya karena desainnya salah. Referensi resmi: OWASP A04 Insecure Design.
Contohnya:
- Endpoint login tanpa rate limit sehingga mudah di-brute-force.
- Trust boundary yang salah — misalnya memutuskan bahwa nilai
roleyang dikirim klien bisa dipercaya. - Proses reset password yang bisa dipakai untuk membajak akun orang lain.
Contoh desain yang salah
Sebuah API mengizinkan siapa pun memanggil endpoint admin berulang-ulang tanpa batasan. Secara implementasi tidak ada bug, tetapi desainnya memungkinkan penyalahgunaan (abuse).
Pencegahan
Threat modeling adalah praktik utama: sebelum menulis kode, tanyakan "bagaimana sistem ini bisa disalahgunakan?" dan tulis asumsi keamanan secara eksplisit. Beberapa kontrol desain yang umum:
- Rate limiting pada endpoint sensitif (login, OTP, search) — lihat CORS, Rate Limit & reCAPTCHA.
- Validasi di sisi server selalu, jangan pernah mempercayai validasi frontend.
- Modelkan trust boundary: data apa yang bisa disentuh pengguna, siapa yang boleh menyentuh apa.
- Gunakan allowlist untuk opsi yang diizinkan, bukan denylist.
// Contoh: batasi jumlah percobaan login per akun
@Component
public class LoginAttemptLimiter {
private final Map<String, Integer> attempts = new ConcurrentHashMap<>();
public boolean isLocked(String email) {
return attempts.getOrDefault(email, 0) >= 5;
}
public void onFailedAttempt(String email) {
attempts.merge(email, 1, Integer::sum);
}
public void onSuccess(String email) {
attempts.remove(email);
}
}
5. A05 — Security Misconfiguration
Ini adalah kategori "kesalahan pengaturan" (OWASP A05). Kode Anda bisa sempurna, tetapi aplikasi tetap rentan karena konfigurasi yang buruk. Ini kategori yang paling sering dijumpai dalam penetration test, dan penyebabnya biasanya sederhana.
Contoh miskonfigurasi umum
| Masalah | Dampak |
|---|---|
Default credentials (misal admin/admin) yang tidak diganti | Siapa pun bisa masuk sebagai admin |
| Stack trace / pesan error lengkap ditampilkan ke pengguna | Membocorkan struktur kode & internal aplikasi |
| Directory listing aktif | Penyerang bisa melihat semua file di folder |
CORS terlalu longgar (Access-Control-Allow-Origin: * untuk endpoint berautentikasi) | Halaman jahat bisa membaca respons API Anda |
| Halaman debug / admin framework tetap aktif | Eksploitasi yang sudah dikenal |
Pencegahan
Hapus mode debug di production dan pastikan konfigurasi berbeda per environment — gunakan configuration profiles dengan @Profile:
@Configuration
@Profile("dev")
public class DevSecurityConfig {
// hanya aktif saat profile "dev", misal H2 console atau logging verbose
}
@Configuration
@Profile("prod")
public class ProdSecurityConfig {
// konfigurasi ketat untuk production
}
Pasang security headers di semua respons, seperti yang direkomendasikan OWASP Secure Headers dan Cheat Sheet Series:
| Header | Nilai contoh | Melindungi dari |
|---|---|---|
X-Frame-Options | DENY | Clickjacking (halaman dimuat dalam iframe) |
Content-Security-Policy | default-src 'self' | XSS dan injeksi konten |
X-Content-Type-Options | nosniff | MIME sniffing |
Strict-Transport-Security | max-age=31536000; includeSubDomains | Menurunkan ke HTTP (HSTS) |
Referrer-Policy | no-referrer | Kebocoran URL lewat header Referer |
Permissions-Policy | geolocation=() | Penggunaan fitur browser oleh pihak ketiga |
Di Spring Security, headers dasar aktif secara default, tapi pastikan nilai CSP Anda tidak kosong:
http.headers(headers -> headers
.xFrameOptions(options -> options.deny())
.contentSecurityPolicy(csp -> csp.policyDirectives("default-src 'self'")));
6. A06 — Vulnerable and Outdated Components
Vulnerable and outdated components artinya aplikasi Anda memakai library, framework, atau dependency yang sudah usang dan diketahui punya celah keamanan (OWASP A06). Setiap dependency adalah kode orang lain yang ikut Anda "deploy" — dan setiap CVE (Common Vulnerabilities and Exposures) yang tidak di-patch adalah pintu yang dibiarkan terbuka.
Contoh
Aplikasi memakai versi lama framework yang ternyata memiliki CVE publik dengan proof-of-concept yang mudah dipakai. Penyerang tidak perlu mencari bug sendiri — cukup memakai exploit yang sudah jadi.
Pencegahan
| Praktik | Cara kerja |
|---|---|
| Update rutin | Ikuti release patch; jangan menunda update keamanan |
| Dependency scanner | Jalankan OWASP Dependency-Check atau OWASP Dependency-Track di CI/CD |
| Pantau CVE | Subscribe ke feed CVE untuk stack yang Anda pakai |
| Kurangi permukaan | Hapus dependency yang tidak terpakai |
Contoh command di terminal (Maven project):
# Periksa dependency terhadap basis data CVE
./mvnw org.owasp:dependency-check-maven:check
Mental model: setiap dependency menambah "luas permukaan serangan". Jangan menambah library hanya karena nyaman — dan jika sudah menambah, Anda bertanggung jawab untuk terus memperbaruinya.
7. A07 — Identification and Authentication Failures
Identification and Authentication Failures terjadi ketika proses verifikasi identitas bisa dikalahkan: password lemah, login bisa di-brute-force, sesi tidak pernah berakhir, atau mekanisme recovery akun bisa disalahgunakan (OWASP A07).
Contoh serangan
- Brute force — menebak password berulang-ulang karena tidak ada batasan percobaan.
- Credential stuffing — mencoba kombinasi
email:passwordyang bocor dari situs lain (karena orang memakai password yang sama di mana-mana). - Sesi tak dibatasi — token/sesi yang tidak pernah kedaluwarsa, bahkan setelah logout.
Pencegahan
- Rate limiting pada endpoint login — lihat CORS, Rate Limit & reCAPTCHA.
- MFA (multi-factor authentication) untuk akun penting.
- Lockout sementara setelah beberapa kali gagal — seperti
LoginAttemptLimiterdi bagian A04. - Session timeout — sesi kedaluwarsa otomatis, terutama untuk aktivitas sensitif.
- Jangan memudahkan email enumeration berlebihan — misalnya perbedaan pesan "email tidak terdaftar" vs "password salah" memberi tahu penyerang bahwa email itu valid. Ini keputusan kebijakan (usability vs security) yang harus disepakati tim.
- Hash password dengan BCrypt — lihat password hashing dengan BCrypt.
// Maksimal satu sesi per akun; sesi kedaluwarsa otomatis
http.sessionManagement(session -> session
.maximumSessions(1)
.expiredUrl("/login?expired"));
// Batasi percobaan login di controller
@PostMapping("/api/auth/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
if (limiter.isLocked(request.getEmail())) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).build();
}
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword()));
limiter.onSuccess(request.getEmail());
return ResponseEntity.ok().build();
} catch (BadCredentialsException ex) {
limiter.onFailedAttempt(request.getEmail());
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
8. A08 — Software and Data Integrity Failures
Software and Data Integrity Failures terjadi ketika kode atau data datang dari sumber yang tidak bisa diverifikasi — supply chain (OWASP A08). Contoh paling sering:
- Plugin atau library yang dimuat tanpa verifikasi identitas.
- Deserialization objek dari input yang tidak dipercaya — penyerang mengirim byte serial yang, saat di-deserialize, mengeksekusi kode jahat.
- CI/CD yang mengambil dependency dari repository tanpa validasi checksum.
Pencegahan
| Praktik | Cara kerja |
|---|---|
| Pin version | Kunci versi dependency yang eksplisit (bukan SNAPSHOT / rentang longgar) |
| Verifikasi checksum | Bandingkan checksum artefak dengan nilai resmi dari publisher |
| Minimalisasi deserialization | Jangan pernah deserialize objek dari input yang tidak dipercaya |
| Review supply chain | Periksa package-lock.json, pom.xml, build.gradle secara rutin |
Contoh buruk — deserialize dari input pengguna:
// SANGAT BERBAHAYA: jangan deserialize data dari input tak dipercaya
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(request.getFile()));
MyObject obj = (MyObject) ois.readObject();
Aturan praktis: jangan pernah memanggil
readObject()/readUnshared()/XMLDecoderpada data yang berasal dari klien atau jaringan. Kalau harus, gunakan format teks aman seperti JSON dengan schema validation, atau allowlist class yang diizinkan.
9. A09 — Security Logging and Monitoring Failures
Tanpa log dan monitoring yang baik, serangan bisa terjadi tanpa disadari (OWASP A09). Banyak insiden besar baru terdeteksi berbulan-bulan setelah terjadi, padahal tanda-tandanya sudah muncul di log. Masalah di kategori ini:
- Gagal login atau akses aneh tidak pernah dicatat.
- Log tersimpan tanpa retention yang jelas, atau justru tidak ada monitoring/alert.
- Log justru mencatat rahasia (password, token) sehingga log yang bocor menjadi kerentanan baru.
Pencegahan
- Log aksi keamanan: login sukses/gagal, reset password, perubahan role, akses ke data sensitif.
- Monitoring + alerting: kirim alert ketika ada pola mencurigakan (misal 10 gagal login dalam 1 menit).
- Jangan log rahasia — redaksi (mask) token, password, dan data pribadi.
// Contoh: log percobaan login gagal dengan konteks yang cukup
@Slf4j
@Component
public class LoginAuditLogger {
public void logFailedLogin(String email, String ip, String reason) {
// catat email, ip, dan alasan; JANGAN pernah catat password/token
log.warn("LOGIN_FAILED email={} ip={} reason={}", email, ip, reason);
}
}
Implementasi lengkapnya — log terstruktur, alerting, dan observability — sudah dibahas di Observability, Logging & Monitoring.
10. A10 — Server-Side Request Forgery (SSRF)
SSRF (Server-Side Request Forgery) terjadi ketika server diminta membuat request ke URL yang bisa dikendalikan penyerang (OWASP A10). Karena request itu datang dari server, ia dipercaya oleh jaringan internal — persis seperti orang yang memakai kartu identitas karyawan.
Contoh serangan
Fitur "fetch gambar dari URL" atau "preview link" biasanya menerima URL dari pengguna. Jika server memanggil URL itu tanpa validasi, penyerang bisa menyuruh server mengakses:
POST /api/preview
{ "url": "http://169.254.169.254/latest/meta-data/" }
169.254.169.254 adalah link-local address untuk metadata cloud (misal AWS, GCP). Dengan SSRF, penyerang bisa mengambil credential/token instance dari alamat ini, lalu menggunakannya untuk masuk lebih dalam. Contoh kode berbahaya:
// BERBAHAYA: server memanggil URL apa pun dari klien
String url = request.getBody().get("url");
URL target = new URL(url);
HttpURLConnection conn = (HttpURLConnection) target.openConnection();
// ... server meminta URL internal atas nama Anda
Pencegahan
| Strategi | Cara kerja |
|---|---|
| Whitelist domain | Hanya izinkan koneksi ke domain yang sudah dikenal |
| Blok IP internal/private | Tolak alamat seperti 127.0.0.1, 10.x, 192.168.x, 169.254.169.254, dan resolve DNS-nya juga |
| Validasi input URL | Wajibkan https://, tolak scheme lain (file://, gopher://) |
| Jangan forward redirect | Setelah koneksi, jangan otomatis mengikuti 3xx ke host lain |
| Segmentasi jaringan | Pisahkan server web dari layanan internal |
// Contoh: validasi URL sebelum membuat request
private static final Set<String> ALLOWED_HOSTS = Set.of("images.example.com");
public boolean isAllowed(URL url) {
if (!"https".equalsIgnoreCase(url.getProtocol())) {
return false;
}
if (!ALLOWED_HOSTS.contains(url.getHost().toLowerCase())) {
return false;
}
return true; // tambahkan juga resolusi DNS dan larangan IP internal
}
Tabel Ringkasan
| # | Nama | Prinsip pencegahan (satu kalimat) |
|---|---|---|
| A01 | Broken Access Control | Selalu cek otorisasi per resource; deny by default, jangan percaya id dari klien. |
| A02 | Cryptographic Failures | Lindungi data sensitif dengan HTTPS/TLS, enkripsi istirahat, dan hashing yang benar. |
| A03 | Injection | Perlakukan input sebagai data, bukan kode — pakai parameterized query. |
| A04 | Insecure Design | Pikirkan penyalahgunaan sejak desain: rate limit, trust boundary, validasi server. |
| A05 | Security Misconfiguration | Hardening konfigurasi: hapus debug, pasang security headers, pisahkan environment. |
| A06 | Vulnerable and Outdated Components | Update dependency rutin dan pantau CVE dengan scanner otomatis. |
| A07 | Identification and Authentication Failures | Batasi percobaan login, aktifkan MFA, dan batasi umur sesi. |
| A08 | Software and Data Integrity Failures | Verifikasi sumber kode/data; jangan deserialize input tak dipercaya. |
| A09 | Security Logging and Monitoring Failures | Log aksi keamanan, pantau dan alert, jangan pernah log rahasia. |
| A10 | SSRF | Batasi ke host yang diizinkan dan blokir akses ke IP internal. |
Cara memakai daftar ini
Jangan berhenti di membaca. Praktikkan dengan cara berikut:
- Threat modeling singkat — sebelum fitur baru, tulis: data apa yang dilindungi, siapa yang boleh menyentuhnya, dan bagaimana sistem bisa disalahgunakan.
- Checklist sebelum rilis — jalankan daftar A01–A10 sebagai review checklist di pull request atau sebelum deploy.
- Prioritaskan berdasarkan data — fokuskan pengujian ke data paling sensitif (kredensial, pembayaran, data pribadi). Tidak semua risiko sama beratnya untuk semua aplikasi.
- Padukan dengan tooling — dependency scanner, static analysis, dan penetration test berkala melengkapi checklist manual ini.
Kesimpulan
Keamanan bukan fitur tambahan yang dipasang di akhir — ia bagian dari desain aplikasi Anda sejak baris pertama. Tidak ada satu kontrol yang cukup: password hashing melindungi A07, parameterized query melindungi A03, cek kepemilikan melindungi A01, dan seterusnya. Inilah yang disebut defense in depth — banyak lapisan sehingga jika satu lapisan gagal, lapisan berikutnya masih melindungi Anda. OWASP Top 10 adalah peta, bukan tujuan; setiap rilis berikutnya, cek lagi apakah Anda masih aman.
Ringkasan
OWASP Top 10 2021 adalah daftar sepuluh risiko web paling kritis yang disusun dari data nyata: dari A01 broken access control (ganti id di URL bisa melihat data orang lain) sampai A10 SSRF (server disuruh memanggil URL internal). Untuk setiap risiko, kuncinya satu prinsip: jangan percaya input dari klien, validasi dan otorisasi di server, lindungi data dengan kriptografi yang benar, jaga konfigurasi tetap hardened, update dependency, dan catat semua aksi keamanan. Keamanan adalah proses berkelanjutan — bukan sekali selesai.