Dalam 15 menit ke depan, kita akan merakit sebuah sistem pertahanan dan optimalisasi regex dari nol sampai berjalan mulus di production. Ingat, CPU server yang mendadak lonjak ke 100% dan membuat service Nginx serta Node.js Anda hang bukan terjadi karena traffic melonjak, melainkan karena ada satu baris regex sembrono yang terjebak dalam jebakan backtracking eksponensial. Mari kita bedah tuntas.
1. Menu Kita Hari Ini & Alat yang Perlu Disiapkan
Pernahkah Anda mendapati aplikasi web berkedip lambat atau bahkan down total saat memvalidasi input user yang panjangnya tidak wajar? Itu adalah ulah ReDoS (Regular Expression Denial of Service). Di dapur arsitektur kita hari ini, kita tidak butuh banyak alat rumit, cukup siapkan terminal Linux (Ubuntu/Debian), Node.js (v18+), Go (v1.20+), dan Nginx di depan sebagai reverse proxy untuk menyaring request kotor sebelum menyentuh backend.
[!IMPORTANT] Pastikan Anda memiliki akses root atau sudo karena kita akan menyetel batasan worker threads dan memantau penggunaan memory secara real-time menggunakan
topatauhtopsaat melakukan benchmark nanti.
2. Peta Jalan Singkat: Apa yang Sedang Kita Rakit
Kita akan memulai perjalanan dengan memahami anatomi ReDoS secara langsung melalui kode yang rusak. Setelah itu, kita menyusun ulang pola tersebut dengan teknik Backtracking Minimization agar mesin V8 di Node.js dan runtime Go kita bisa bernapas lega. Terakhir, kita uji ketahanannya di bawah tekanan ribuan concurrent request.
[!TIP] Jangan lewatkan bagian perbandingan antara mesin V8 dan Go, karena penanganan catastrophic backtracking di kedua runtime ini memiliki karakteristik yang sangat berbeda.
3. Langkah 1: Mempersiapkan Fondasi & Environment
Mari kita buat direktori kerja baru di server lokal atau development machine Anda, lalu inisialisasi project Node.js dan Go secara berdampingan agar kita bisa melihat perbandingan langsung kinerjanya.
mkdir -p redos-lab/node redos-lab/go
cd redos-lab/node
npm init -y
npm install express autocannon
Jalankan perintah di atas, dan lihat responsnya di terminal. Jika paket autocannon dan express terinstal tanpa error, itu tanda bahwa fondasi testing environment kita sudah siap menampung beban simulasi serangan.
4. Langkah 2: Meracik Konfigurasi Inti Tanpa Basa-basi
Sekarang, mari kita buat server Express sederhana di Node.js yang menggunakan pola regex rentan, lalu kita ubah menjadi regex yang aman.
Buat file server.js di dalam folder node/:
const express = require('express');
const app = express();
// POLA BERBAHAYA: Mengandung nested quantifiers (a+)+ yang memicu ReDoS
// Contoh input jahat: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa!'
const vulnerableRegex = /^(a+)+$/;
// POLA AMAN: Dihilangkan pengulangan bersarang, menggunakan atomic group atau linear check
const secureRegex = /^a+$/;
app.get('/test', (req, res) => {
const input = req.query.input || '';
const start = process.hrtime();
// Ubah vulnerableRegex menjadi secureRegex untuk melihat perbedaan performa drastis
const isMatch = secureRegex.test(input);
const diff = process.hrtime(start);
const timeInMs = (diff[0] * 1e9 + diff[1]) / 1e6;
res.json({ match: isMatch, executionTimeMs: timeInMs });
});
app.listen(3000, () => {
console.log('ReDoS Lab running on port 3000');
});
Jalankan server dengan node server.js. Begitu server menyala, kita telah berhasil menempatkan jebakan dan penawarnya secara bersamaan.
5. Langkah 3: Sentuhan Akhir Keamanan & Optimasi
Untuk melengkapi arsitektur, mari kita lihat bagaimana Go menangani validasi serupa menggunakan package regexp bawaan yang secara desain menolak backtracking tak terbatas demi mencegah ReDoS.
Buat file main.go di dalam folder go/:
package main
import (
"fmt"
"net/http"
"regexp"
"time"
)
func main() {
// Go menggunakan engine RE2 yang menjamin eksekusi linear O(n)
secureRegex := regexp.MustCompile(`^a+$`)
http.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
input := r.URL.Query().Get("input")
start := time.Now()
_ = secureRegex.MatchString(input)
duration := time.Since(start)
fmt.Fprintf(w, "Execution time: %s\n", duration)
})
fmt.Println("Go ReDoS Lab running on port 8080")
_ = http.ListenAndServe(":8080", nil)
}
[!WARNING] Jangan pernah menggunakan library regex pihak ketiga di Go yang mendukung lookaround kompleks atau backreferences, karena hal tersebut sering kali mengembalikan celah backtracking khas Perl/PCRE ke dalam ekosistem Go.
6. Tes Rasa: Memastikan Semuanya Berjalan Sempurna
Saatnya menguji hasil racikan kita. Gunakan autocannon untuk menghujani endpoint Node.js kita dengan payload yang panjang.
npx autocannon -c 100 -d 10 'http://localhost:3000/test?input=aaaaaaaaaaaaaaaaaaaaaaaaaaaa'
Perhatikan metrik latency dan requests per second. Jika menggunakan regex yang aman (secureRegex), latensi akan tetap berada di bawah 2 milidetik. Namun, jika Anda menggantinya kembali ke vulnerableRegex dan mengirim string yang gagal dicocokkan dengan karakter penutup yang salah, Anda akan melihat server mendadak macet dan throughput terjun bebas hingga 0 req/sec.
7. Pertolongan Pertama: Jika Terjadi Kendala Kecil
Jika saat melakukan benchmark Anda mendapati error EMFILE: too many open files, itu artinya Nginx atau sistem operasi Anda kehabisan alokasi file descriptor. Cukup sesuaikan batas maksimum sistem dengan perintah ulimit -n 65535 di terminal Linux Anda, lalu restart service web server atau aplikasi Node.js Anda. Selamat mencoba di environment staging Anda!