# Seni Menulis Regular Expression Kompleks yang Aman dari ReDoS

> Diterbitkan oleh Literally Web ID pada 20 Oct 2026
> Sumber: https://literally.web.id/articles/seni-menulis-regular-expression-kompleks-yang-aman-dari-redos

---

## 1. Alarm Tengah Malam: Saat Layanan Produksi Mulai Tumbang

Pukul 02.14 WIB. Ponsel saya berdering melengking, memecah keheningan malam lewat notifikasi PagerDuty yang menandakan metrik *latency* layanan *backend* utama kita melonjak drastis melewati ambang batas kritis 5000ms. Sebagai Principal Systems Architect yang baru saja memejamkan mata setelah seharian memikirkan *scaling* infrastruktur Kubernetes, adrenalin langsung melonjak. Saya membuka laptop, menyalakan VPN, dan mendarat langsung di dashboard Grafana. Apa yang saya lihat sungguh membuat bulu kuduk merinding: grafik *CPU utilization* di seluruh kluster Node.js dan Go menembus 100%, sementara *memory usage* merangkak naik perlahan namun pasti menuju titik *Out of Memory (OOM) Killer*.

```bash
# Pengecekan cepat load average pada container pod yang sekarat
kubectl top pods -n production --sort-by=cpu
```

Di saat yang sama, Nginx *ingress controller* melaporkan lonjakan *connection timeout* yang masif. Ribuan *client* dari berbagai belahan dunia mendapati halaman web berputar-putar tanpa akhir (*infinite loading*). Beban kerja yang biasanya didistribusikan secara merata oleh Cloudflare CDN tiba-tiba tertahan di *gateway* backend karena beberapa *endpoint* validasi input mendadak tidak merespons. Namun, ini bukanlah serangan DDoS berbasis volume jaringan tradisional dengan *SYN flood* atau *HTTP GET flooding*. Ini adalah jenis pembunuhan sistem yang jauh lebih senyap, elegan, namun mematikan: sebuah serangan presisi yang mengeksploitasi kelemahan fundamental dalam cara kita memproses string teks. Penasaran dengan apa yang sebenarnya menguras habis siklus CPU server kita tanpa ampun, saya langsung membuka terminal untuk menelusuri jejak digital di balik dinding log sistem.

## 2. Jejak Digital: Menelusuri Anomali Log & Lonjakan Metrik

Berbekal akses root ke server log aggregator kita, langkah pertama saya adalah menyaring baris-baris log dari PHP-FPM dan Node.js API untuk mencari pola yang tidak biasa. Saya menjalankan perintah `tail` dan `grep` untuk membedah *access log* Nginx yang sedang megap-megap menerima ribuan koneksi konkuren.

```bash
# Memfilter request HTTP yang memicu latensi tinggi pada Nginx access log
awk '($9 ~ /502|504/)' /var/log/nginx/access.log | tail -n 50
```

Hasilnya langsung menunjukkan sebuah pola yang sangat spesifik. Semua request yang macet berasal dari *endpoint* registrasi pengguna dan formulir pencarian produk yang menerima input string panjang dari pengguna. Menariknya, tidak ada error database, tidak ada lonjakan query MySQL yang lambat, dan Redis *cache hit ratio* kita berada di angka normal 99%. Masalahnya murni terisolasi di dalam lapisan pemrosesan aplikasi (*application layer*). 

> [!WARNING]
> Jangan pernah meremehkan string input dari *client*. Seringkali, penyerang memanfaatkan *payload* string yang dirancang khusus untuk memicu *pathological worst-case behavior* pada fungsi *parsing* teks di aplikasi kita.

Untuk membuktikan hipotesis ini, saya mengambil sampel *payload* mencurigakan dari log dan mengujinya secara lokal menggunakan alat *profiling* CPU. Di sinilah saya menemukan kenyataan pahit: sebuah baris kode yang terlihat sepele untuk memvalidasi format email atau nomor seri produk ternyata memakan waktu eksekusi hingga 15 detik hanya untuk satu string input sepanjang 40 karakter. Untuk memahami mengapa sepotong string pendek bisa melumpuhkan inti CPU, kita harus menyelami anatomi dari bencana itu sendiri, yaitu mekanisme di balik bagaimana mesin pencocokan pola bekerja.

## 3. Membongkar Biang Kerok: Akar Masalah yang Sebenarnya

Mari kita bedah apa yang sebenarnya terjadi di balik layar. Masalah utama yang melumpuhkan infrastruktur kita malam itu adalah apa yang disebut sebagai *Catastrophic Backtracking* dalam ekspresi reguler atau yang lebih dikenal dengan ReDoS (*Regular Expression Denial of Service*). 

> [!IMPORTANT]
> ReDoS terjadi ketika mesin *regex* non-deterministik (NFA - *Nondeterministic Finite Automaton*) mencoba mencocokkan pola ambigu dengan input yang dirancang khusus, menyebabkan kompleksitas waktu melonjak secara eksponensial dari $O(N)$ menjadi $O(2^N)$.

Ambil contoh pola regex yang naif namun sering dijumpai di blog-blog tutorial untuk mencocokkan string berulang:

```javascript
// CONTOH BERBAHAYA: Jangan pernah gunakan pola seperti ini di production!
const dangerousRegex = /^([a-zA-Z]+)*$/;
```

Jika kita memberikan string normal seperti `abcdefghijklmnopqrstuvwxyz`, mesin *regex* akan menyelesaikannya dalam beberapa mikrodetik. Namun, berikan string yang sedikit melenceng di akhir, misalnya `abcdefghijklmnopqrstuvwxyz!`, mesin NFA akan mulai melakukan *backtracking*—mundur ke langkah sebelumnya untuk mencoba kombinasi jalur lain secara membabi buta. Karena ada kuantifikator bersarang (`([a-zA-Z]+)*`), jumlah kombinasi jalur pencocokan tumbuh secara eksponensial. Satu karakter tambahan di ujung string dapat melipatgandakan siklus CPU yang dibutuhkan. 

Dalam ekosistem modern kita, masalah ini diperparah oleh perbedaan perilaku antar *runtime*. Mesin V8 di Node.js dan *engine* bawaan di Go menangani *backtracking* secara agresif untuk fitur-fitur canggih seperti *lookbehind* dan *backreferences*. Ketika beban *request* masuk secara bersamaan, setiap *worker thread* atau *goroutine* terjebak dalam lingkaran setan komputasi ini, membuat *event loop* Node.js membeku dan scheduler Go kehabisan sumber daya. Sebelum kita merancang strategi pencegahan jangka panjang, kita harus segera memadamkan api yang sedang membakar infrastruktur saat ini.

## 4. Tindakan Darurat: Menghidupkan Kembali Layanan dalam Hitungan Menit

Situasi sudah sangat kritis. Pelanggan mulai mengeluh di media sosial, dan tim manajemen mulai menghubungi saya via telepon darurat. Kita tidak punya waktu untuk melakukan refaktorisasi kode yang kompleks saat ini. Langkah pertama yang harus diambil adalah menerapkan mitigasi instan di lapisan *edge* dan *gateway* sebelum mereka mencapai server *backend* aplikasi.

Segera saya masuk ke dashboard Cloudflare dan menerapkan *WAF Custom Rule* dadakan untuk memblokir atau menolak *request* yang panjang *string*-nya melebihi batas wajar pada *endpoint* rentan. Selain itu, saya juga memperbarui konfigurasi Nginx *reverse proxy* di server kita untuk membatasi ukuran *payload* HTTP yang masuk.

```nginx
# Konfigurasi darurat di Nginx untuk membatasi ukuran body dan timeout
http {
    client_max_body_size 10k; # Batasi ukuran payload secara agresif
    client_body_timeout 5s;   # Putus koneksi jika client lambat mengirim data
    client_header_timeout 5s;
    
    server {
        # Blokir request dengan pola string mencurigakan di level Nginx map
        if ($arg_q ~* "([a-zA-Z]+)*") {
            return 403;
        }
    }
}
```

Di sisi aplikasi backend, saya segera mendorong *hotfix* darurat melalui *pipeline* CI/CD untuk membungkus eksekusi *regex* yang bermasalah dengan fungsi pembatas waktu eksekusi (*timeout wrapper*), atau menggantinya sementara dengan validasi berbasis fungsi string bawaan yang aman dari kompleksitas eksponensial.

```go
// Solusi darurat di Go: Membatasi panjang input sebelum masuk ke fungsi validasi
func ValidateInputSafely(input string) bool {
    if len(input) > 64 {
        // Tolak seketika jika input terlalu panjang untuk mencegah backtracking
        return false
    }
    // Lanjutkan dengan logika validasi non-regex yang aman
    return true
}
```

Hanya dalam waktu 6 menit setelah eksekusi perintah darurat tersebut, grafik metrik mulai menunjukkan penurunan beban. CPU *utilization* turun dari 100% kembali ke zona aman di bawah 30%, dan layanan berangsur pulih sepenuhnya. Namun, insiden malam ini memberikan pelajaran mahal. Kita harus memastikan hal serupa tidak pernah terulang kembali melalui kebijakan arsitektur yang defensif.

## 5. Kebijakan Retrospektif: Menambal Celah agar Tak Pernah Terulang

Setelah matahari terbit dan situasi benar-benar kondusif, saya memimpin sesi *post-mortem* bersama tim engineering. Kami menyepakati bahwa pendekatan penulisan *regex* di masa lalu terlalu longgar dan mengandalkan asumsi bahwa semua input pengguna bersifat jinak. Mulai hari ini, kita memberlakukan standar operasional prosedur (SOP) baru dalam penulisan dan peninjauan kode (*code review*).

Pertama, kita melarang keras penggunaan kuantifikator bersarang (`(a+)*`, `(a*)*`, atau `(a|ab)+`) di seluruh basis kode kita, baik itu di PHP, Go, maupun JavaScript. Kedua, setiap ekspresi reguler yang kompleks kini wajib melalui proses pengujian otomatis menggunakan alat analisis statis khusus ReDoS sebelum dapat digabungkan ke cabang utama *repository* Git.

Berikut adalah perbandingan pola *regex* yang berbahaya versus pola aman yang kini wajib digunakan oleh seluruh tim:

| Kategori | Pola Berbahaya (Rentan ReDoS) | Pola Aman (Linear / Atomic) | Alasan Teknis |
| :--- | :--- | :--- | :--- |
| **Validasi Email** | `^([a-zA-Z0-9_.-])+@([a-zA-Z0-9_.-])+.([a-zA-Z]{2,6})+$` | `^[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.-]+.[a-zA-Z]{2,6}$` | Menghilangkan kelompok tangkapan luar yang bersarang (`+` di luar tanda kurung). |
| **Pencarian Kata** | `^(a+)+b$` | `^a+b$` | Menghindari pengulangan grup yang memicu *backtracking* eksponensial. |
| **Validasi URL** | `^(https?://)?([a-zA-Z0-9.-]+)+(/[a-zA-Z0-9._~:/?#[]@!$&'()*+,;=]*)?$` | Gunakan *URL Parser* bawaan bahasa pemrograman | Pemrosesan URL kompleks sebaiknya tidak menggunakan *regex* mentah. |

Selain aturan penulisan kode, kita juga mengintegrasikan pengujian keamanan otomatis ke dalam *pipeline* integrasi berkelanjutan (*CI/CD*) menggunakan utilitas pemindai ReDoS berbasis *command line* yang berjalan di setiap *Pull Request* baru.

## 6. Pasang Alarm: Otomasi Deteksi Dini untuk Tim On-Call

Agar kita tidak lagi bergantung pada kepanikan tengah malam atau laporan pengguna yang marah, langkah terakhir yang saya implementasikan adalah membangun sistem peringatan dini yang proaktif. Kita memasang metrik pemantauan khusus di Prometheus untuk melacak durasi eksekusi fungsi pencocokan teks dan memicu *alert* otomatis jika ada thread yang tertahan lebih dari 50 milidetik.

```yaml
# Contoh definisi Prometheus Alert untuk mendeteksi potensi lonjakan latensi regex
groups:
- name: ReDoSProtectionAlerts
  rules:
  - alert: HighRegexExecutionLatency
    expr: rate(app_regex_execution_duration_seconds_sum[5m]) / rate(app_regex_execution_duration_seconds_count[5m]) > 0.05
    for: 2m
    labels:
      severity: critical
    annotations:
      summary: "Potensi ReDoS terdeteksi pada layanan backend"
      description: "Waktu eksekusi rata-rata untuk pencocokan pola melebihi 50ms selama 2 menit terakhir."

```

Dengan aturan *alerting* ini, sistem kini dapat mendeteksi gejala awal adanya upaya eksploitasi atau *bug* pola regex sebelum ia sempat melumpuhkan seluruh kluster server. Pengalaman malam ini mengajarkan kita bahwa arsitektur web yang tangguh bukan hanya soal seberapa besar kapasitas server yang kita sewa di cloud, tetapi juga seberapa cermat kita memperlakukan setiap baris kode kecil yang memproses data dari dunia luar. Dan kini, dengan sistem pertahanan berlapis yang baru, saya bisa kembali tidur dengan tenang, setidaknya sampai alarm berikutnya berdering.