🧠 Regular Expressions β€” Deep Dive: Quantifiers, Groups, Lookahead, Performance, dan Detection Engineering

Panduan komprehensif regular expressions dari perspektif security engineer. Regex adalah fondasi dari semua detection tool di vault ini β€” Sigma rules, YARA rules, Suricata IDS rules, WAF rules (ModSecurity, Cloudflare), log parsing, grep, sed, awk. Tanpa paham regex secara fundamental, lo cuma copy-paste rule tanpa ngerti kenapa rule itu work β€” atau lebih parah, kenapa rule itu gak detect attack yang seharusnya. Mencakup engine types (PCRE, RE2, Rust regex), quantifiers, groups & backreferences, lookahead/lookbehind, atomic groups & possessive quantifiers, backtracking & catastrophic backtracking (ReDoS), regex optimization, dan praktik konkret untuk detection engineering.

Posisi di Vault

Nota ini adalah alat yang dipake di hampir semua catatan lain: Sigma rules di incident-response-framework, malware-analysis-reverse-engineering-playbook (YARA rules), blueteam-detection-matrix (detection rules), cloudflare-ruleset-engine-phases (WAF rules), web-hacking-exploitation (bypass WAF dengan regex), ids-ips-waf-nsm-comparison (Suricata rules). Juga terhubung ke http-protocol-deepdive (request parsing) dan encoding-serialization-compression-deepdive (regex untuk encoding detection).


Daftar Isi


Foundation

Apa Itu Regex?

Regular Expression (regex) adalah pola yang mendeskripsikan sekumpulan string. Bukan bahasa pemrograman β€” tapi notation untuk pattern matching.

Pola:    ^[A-Z]{3}-\d{4}$
Cocok:   ABC-1234
         XYZ-9876
Tidak:   abc-1234  (lowercase)
         ABC-12345 (5 digit)
         ABC1234   (tanpa -)

Komponen Dasar

KomponenContohMaksud
LiteralhelloCocok exact string β€œhello”
Doth.lloSatu karakter apapun (hallo, hxllo, h5llo)
Character class[aeiou]Satu karakter vokal
Negated class[^aeiou]Satu karakter BUKAN vokal
Quantifier\d{3}Tepat 3 digit
Alternationcat|dog”cat” ATAU β€œdog”
Group(ab)+”ab” satu atau lebih (ab, abab, ababab)
Anchor^start”start” di AWAL string
Shorthand\w+Satu atau lebih word character

Quantifiers

Greedy, Lazy, Possessive

String: "<div>hello</div><span>world</span>"
 
Pola: <.+>      # Greedy β€” cocok sepanjang mungkin
Match: <div>hello</div><span>world</span>  # (entire string!)
 
Pola: <.+?>     # Lazy β€” cocok sependek mungkin
Match: <div>   # (cuma tag pertama)
 
Pola: <[^>]+>   # Praktis β€” "everything except >"
Match: <div>, </div>, <span>, </span>  # (4 matches!)
QuantifierGreedyLazyPossessiveMaksud
***?*+0 atau lebih
+++?++1 atau lebih
?????+0 atau 1
{n}{n}{n}?{n}+Tepat n
{n,}{n,}{n,}?{n,}+Minimal n
{n,m}{n,m}{n,m}?{n,m}+n sampai m

Greedy (default): Engine coba match sebanyak mungkin, lalu backtrack kalo gagal. Lazy: Engine coba match sesedikit mungkin, lalu expand kalo gagal. Possessive: Sama kayak greedy tapi gak backtrack β€” kalo gagal, langsung fail (cegah catastrophic backtracking).


Character Classes & Shorthands

Custom Classes

[abc]       # Satu karakter: a, b, atau c
[a-z]       # Satu lowercase letter
[0-9]       # Satu digit
[a-zA-Z0-9]  # Satu alphanumeric
[^0-9]      # BUKAN digit
 
# Special chars inside class β€” escaped or placed right
[\^a-b]     # ^ di awal = literal, bukan negasi
[a-b\-c]    # - di akhir atau escaped = literal dash
[\]]        # ] harus di-escape

Shorthand Classes

ShorthandSetara DenganCocok
\d[0-9]Digit
\w[a-zA-Z0-9_]Word character (letter, digit, underscore)
\s[ \t\n\r\f\v]Whitespace
\D[^0-9]Bukan digit
\W[^a-zA-Z0-9_]Bukan word character
\S[^ \t\n\r\f\v]Bukan whitespace

\w β‰  only letters

Di banyak engine (PHP, Java, .NET, Python), \w juga cocok Unicode letters β€” bukan cuma ASCII. Kalo mau ASCII-only, pake [a-zA-Z0-9_].


Groups & Backreferences

Capturing Groups

(\d{3})-(\d{4})     # Dua group: $1 = 3 digit, $2 = 4 digit
(\w+)@(\w+\.\w+)    # Email: $1 = name, $2 = domain

Referencing:

EngineSyntaxContoh
Dalam pola\1(a)b\1 β†’ match β€œaba”
Dalam replacement$1sed 's/(foo)/$1bar/'
Python\g<1>re.sub(r'(foo)', r'\g<1>bar', s)
JavaScript$1"foo".replace(/(f)/, "$1oo")

Backreference example: Deteksi kata berulang:

\b(\w+)\s+\1\b    # Cocok: "hello hello", "test test"

Non-Capturing Groups

(?:pattern)   # Group TAPI gak disimpan di memory
# Cocok buat alternation tanpa overhead capturing:
(?:foo|bar) v baz   # foo v baz ATAU bar v baz
# Vs: (foo|bar) v baz β€” sama, tapi foo/bar gak di-capture

Named Groups

(?P<name>\d+)     # Python
(?<name>\d+)      # PCRE, .NET, PHP, Java 7+
(?'name'\d+)      # .NET alternatif
 
# Referencing:
\k<name>          # Dalam pola (PCRE)
(?P=name)         # Dalam pola (Python)
$+{name}          # Dalam replacement (.NET)

Anchors & Boundaries

AnchorCocok diContoh
^Awal string (atau awal baris dengan flag m)^Hello β€” string dimulai β€œHello”
$Akhir string (atau akhir baris)world$ β€” string diakhiri β€œworld”
\bBoundary word/non-word\bword\b β€” β€œword” utuh, bukan β€œpassword”
\BBUKAN word boundary\Bword β€” β€œword” di tengah kata: β€œpassword”
\AAwal string (IGNORE multiline flag)\AHello
\ZAkhir string (atau newline di akhir)world\Z
\zAkhir string (BENAR-BENAR akhir)world\z

Lookahead & Lookbehind

Lookaround adalah zero-width assertion β€” cocok posisi, bukan karakter.

TypeSyntaxContohMaksud
Positive lookahead(?=...)\d(?=px)Digit yang diikuti β€œpx”: 5px β†’ match 5
Negative lookahead(?!...)\d(?!px)Digit yang TIDAK diikuti β€œpx”
Positive lookbehind(?<=...)(?<=\$)\d+Angka setelah $: $100 β†’ match 100
Negative lookbehind(?<!...)(?<!\$)\d+Angka yang TIDAK didahului $

Contoh Praktis

# Password validation (8+ chars, at least 1 uppercase, 1 digit, 1 special)
^(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*]).{8,}$
 
# Positive lookahead: q tapi bukan qu
q(?!u)            # Cocok "q" di "Iraq" tapi bukan di "quick"
 
# Negative lookbehind: "foo" tapi gak didahului "bar"
(?<!bar)foo       # Cocok "foo" di "foo" dan "Xfoo" tapi bukan di "barfoo"
 
# Extract value dari JSON key (lookbehind + lookahead)
(?<="password":\s*")[^"]+
# Cocok: "password": "hunter2" β†’ match "hunter2"

Keterbatasan: Lookbehind di sebagian engine (JavaScript, Python re) harus fixed-length β€” (?<=\d{3}) boleh, (?<=\d+) gak boleh. PCRE, Python regex library, .NET support variable-length lookbehind.


Atomic Groups & Possessive Quantifiers

Atomic Group

(?>pattern)

Atomic group: kalo pattern di dalamnya cocok, engine gak akan backtrack ke dalam group ini. Berguna untuk (1) optimasi performa, (2) cegah backtracking yang gak perlu, (3) cegah catastrophic backtracking.

Contoh:

String: "aaaaa"
Pola 1: (a+|b)  → Match "aaaaa" (greedy, lalu coba b — gagal, lalu backtrack a→aaaa, dll)
Pola 2: (?>a+|b) β†’ Match "aaaaa" (gaco aaaaa, coba b β€” gagal β€” LANGSUNG fail tanpa backtrack)

Tanpa atomic: backtracking mencoba aaaaa, aaaa, aaa, aa, a β€” 5 langkah. Dengan atomic: langsung aaaaa β€” 1 langkah.

Possessive Quantifier

Sama dengan atomic group β€” quantifier yang gak backtrack:

.*+       # Sama kayak (?>.*)
\w++
\d{3,5}+

Kapan pake: Ketika lo tahu bahwa setelah quantifier, pola yang cocok gak akan berubah dengan backtracking. Contoh: \d++[a-z] β€” kalo digit udah cocok, backtracking digit gak akan bikin [a-z] cocok.


Flags

FlagPythonPCREJavaScriptFungsi
Case insensitivere.Iii[a-z] cocok uppercase juga
Multilinere.Mmm^ dan $ cocok per baris, bukan seluruh string
Dotallre.Sss. cocok newline (\n) juga
Unicodere.Uuu\w, \d dll treat string sebagai Unicode
Verbosere.XxxAllow whitespace + comments dalam pola
ASCIIre.Aβ€”β€”Force \w, \d, \s ke ASCII-only

Contoh Verbose Mode

import re
pattern = re.compile(r"""
    ^                 # Start of string
    (?=.*[A-Z])       # At least one uppercase
    (?=.*[a-z])       # At least one lowercase
    (?=.*\d)          # At least one digit
    .{8,}             # At least 8 characters
    $                 # End of string
""", re.VERBOSE)

Engine Types

DFA vs NFA

EngineCara KerjaKecepatanFiturContoh
DFA (Deterministic Finite Automaton)Linear β€” setiap karakter diproses sekali🟒 Sangat cepat, O(n)❌ No backreferences, no lookaround, no capturing groupsawk, grep (default), lex
Traditional NFABacktracking β€” coba semua kemungkinan🟑 Bisa lambat (exponential di worst case)βœ… Full fitur: groups, backref, lookaroundPCRE, Python re, JavaScript, Java, .NET, PHP
POSIX NFABacktracking β€” cari longest matchπŸ”΄ Paling lambat🟑 Groups, no backrefPOSIX regex.h, beberapa implementasi sed
RE2 (Hybrid)Automata-based + limited backtracking🟒 O(n) guaranteed β€” gak ada ReDoS🟑 No backreferences, no lookaround (some)Go regexp, Rust regex, RE2 library

Kapan Pilih Engine

KebutuhanEngine yang Cocok
Log parsing (jutaan baris, pola sederhana)RE2, DFA β€” performa stabil
User input validation (potensi ReDoS)RE2 β€” gak bisa catastrophic backtracking
Detection rules (butuh backreferences, lookaround)PCRE β€” fitur lengkap
YARA rulesYARA pake PCRE-compatible engine
Suricata rulesPCRE (fitur lengkap) β€” tapi hati-hati ReDoS
Sigma rulesFormat agnostik β€” bisa di-export ke berbagai backend

Catastrophic Backtracking β€” ReDoS

Penyebab

Terjadi ketika regex punya nested quantifiers dengan overlapping matches β€” engine backtrack eksponensial.

Pola klasik:

^(a+)+$      # Nested quantifiers β€” a+ di dalam ( )+
^(\d+)*$     # Digit di dalam group dengan *
(a|aa)+      # Alternation dengan overlap
(a*)*b       # Star di dalam star

Eksponensial:

String: "aaaaaaaaaaaaaaaaaaaaaa" (22 a's)
Pola: ^(a+)+$

Step ketika string TIDAK cocok (misal ada trailing "x"):
1. (a+) coba 22 a's β†’ ( )+ coba 1Γ— β†’ gagal di x
2. Backtrack: (a+) β†’ 21 a's β†’ ( )+ β†’ 2Γ— β†’ (a+) β†’ 1 a β†’ 2Γ— β†’ gagal
3. Backtrack: (a+) β†’ 21 a's β†’ ( )+ β†’ 1Γ— β†’ gagal
4. Backtrack: (a+) β†’ 20 a's β†’ ( )+ β†’ 3Γ— β†’ (a+) β†’ 1 a β†’ 1Γ— β†’ ...
... exponensial!

Hasil: 22 karakter β†’ ~2^22 = 4 juta langkah β†’ ReDoS (Regex Denial of Service).

Contoh Real-World

CVEPolaDampak
CVE-2021-39227(\w+[,.])+ di OWASP ESAPI validatorStack overflow β†’ DoS
Cloudflare ReDoS (2019).*.*=.* di Cloudflare WAF ruleCPU 100% β†’ global outage 27 menit
Node.js slug (2022)([a-zA-Z0-9-]+)+ReDoS via crafted input

Mitigasi

# 1. Gunakan atomic group
^(?>a+)+$       # Atomic β€” gak backtrack, langsung fail
 
# 2. Gunakan possessive quantifier
^a++$           # Possessive β€” sama
 
# 3. Hindari nested quantifiers
# ❌ (a+)+       β†’ nested
# βœ… aa*         β†’ tanpa nested, sama aja
 
# 4. Gunakan boundary / anchor
# ❌ (\d+)*      β†’ berbahaya
# βœ… \d+         β†’ anchor atau boundary lebih jelas
 
# 5. Gunakan RE2 kalo gak butuh backreference
# Go, Rust, re2 library β€” O(n) guaranteed
 
# 6. Timeout di semua regex execution
import re
# Python: re.compile(pattern) β€” no timeout built-in β†’ butuh signal
# Gunakan library regex dengan timeout: pip install regex
# regex.Regex(pattern, timeout=0.5)
 
# Di Suricata: pcre_match_limit dan pcre_match_limit_recursion
# Di Cloudflare WAF: OWASP CRS dengan batas backreference

ReDoS Detection

# Test pola dengan input bertambah
regex_test() {
    local pattern="$1"
    for i in 10 15 20 25; do
        local input=$(python3 -c "print('a' * $i + 'x')")
        echo -n "Length $i: "
        time (echo "$input" | grep -P "$pattern" 2>/dev/null) 2>&1 | grep real
    done
}
regex_test '(a+)+$'

Regex Optimization

Slow vs Fast

# ❌ Slow
r"<.*>"                # Greedy β†’ backtrack sepanjang string
r"(\d+|\w+)+"          # Nested alternation + quantifier
 
# βœ… Fast
r"<[^>]*>"             # Specific character class β€” no backtrack
r"\w+"                 # Single quantifier β€” gak nested

Prinsip Optimasi

  1. Specific over generic: [a-z] lebih cepat dari \w (karena \w = [a-zA-Z0-9_] + Unicode)
  2. Anchor early: ^... β€” kalau gagal di awal, gak perlu scan seluruh string
  3. Unroll loops: (a|b|c)+ β†’ [abc]+ (character class faster than alternation)
  4. Avoid backtracing: pake atomic group (?>...) atau possessive ++
  5. Failure fast: ^(?!.*evil) β€” cegah moreksekusi dengan negative lookahead di awal
  6. Use specific quantifiers: {3} instead of + kalo tahu ukuran pasti

Benchmark Comparison

import time, re
 
data = "a" * 100000 + "b"
 
# Slow regex
start = time.time()
re.match(r"(a+)+b", data)
print(f"Slow: {time.time() - start:.2f}s")  # ~5-10s β€” catastrophic backtracking
 
# Fast regex
start = time.time()
re.match(r"a++b", data)
print(f"Fast: {time.time() - start:.4f}s")  # ~0.0001s β€” no backtrack

Regex untuk Detection Engineering

Sigma Rules

# Sigma rule β€” PowerShell download cradle
detection:
  selection:
    ScriptBlockText|contains:
      - "System.Net.WebClient"
      - "Invoke-WebRequest"
  condition: selection
# Di-export ke Elasticsearch:
# event.code: "4104" AND winlog.event_data.ScriptBlockText: (*System.Net.WebClient* OR *Invoke-WebRequest*)
# Atau ke Splux:
# index=windows source="WinEventLog:Microsoft-Windows-PowerShell/Operational"
# ScriptBlockText=*System.Net.WebClient* OR ScriptBlockText=*Invoke-WebRequest*

YARA Rules

rule APT_Malware_Domain {
    strings:
        $c2 = /https?:\/\/([a-z0-9-]+\.){2,}[a-z]{2,}\/([a-zA-Z0-9\/=]+)?(\?[a-z0-9=]+)?/
    condition:
        $c2
}

Suricata Rules

# Suricata β€” HTTP SQL injection detection
alert http any any -> any any (
    msg:"ET WEB_SERVER SQL Injection in URI";
    flow:to_server,established;
    content:"%27"; http_uri;           # URL-encoded single quote
    content:"OR"; http_uri; nocase;
    pcre:"/(\bSELECT\b|\bUNION\b|\bINSERT\b|\bDROP\b)/i";
    classtype:web-application-attack;
    sid:1000002;
)

WAF Rules (ModSecurity)

# ModSecurity β€” SQL injection via regex
SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "@rx (?i:(?:union)\s+(?:all|select|distinct))" \
    "id:942100,phase:2,deny,status:403,msg:'SQL Injection - UNION SELECT Detected'"

Practical Patterns for Security

# IP Address
\b(?:\d{1,3}\.){3}\d{1,3}\b
 
# Email
[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}
 
# URL
https?://[^\s/$.?#].[^\s]*
 
# File path (Windows)
[a-zA-Z]:\\(?:[^\\]+\\?)*
 
# Hexadecimal string (malware indicator)
\b[0-9a-fA-F]{32,}\b
 
# Base64 encoded string (C2 config, token)
\b[A-Za-z0-9+/]{40,}={0,2}\b
 
# DGA domain (DGA-generated)
\b[a-z]{8,20}\.(com|net|tk|cf|ga|ml)\b
 
# JWT Token
eyJ[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*

Koneksi ke Vault


References

  1. Friedl, Jeffrey. Mastering Regular Expressions, 3rd Ed. O’Reilly, 2006. β€” Buku wajib untuk regex.
  2. Regular-Expressions.info. Regex Tutorial. https://www.regular-expressions.info/
  3. PCRE. PCRE Documentation. https://www.pcre.org/original/doc/html/
  4. RE2. RE2 Syntax. https://github.com/google/re2/wiki/Syntax
  5. OWASP. Regular Expression Denial of Service (ReDoS). https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
  6. Cloudflare. How Cloudflare mitigates ReDoS. https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019/
  7. SIGMA. Sigma Rule Specification. https://github.com/SigmaHQ/sigma-specification
  8. YARA. YARA Documentation. https://yara.readthedocs.io/en/stable/
  9. Suricata. Suricata Rule Syntax. https://suricata.readthedocs.io/en/latest/rules/intro.html
  10. ModSecurity. ModSecurity Reference Manual. https://github.com/SpiderLabs/ModSecurity/wiki
  11. Python. re module documentation. https://docs.python.org/3/library/re.html
  12. MDN. JavaScript RegExp. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
  13. Rust. regex crate. https://docs.rs/regex/latest/regex/
  14. Go. regexp package. https://pkg.go.dev/regexp
  15. Regex101. Online Regex Tester. https://regex101.com/
  16. Debuggex. Visual Regex Debugger. https://www.debuggex.com/

Bottom Line

Regex adalah senjata paling tajam dan paling berbahaya di toolbox security engineer. Satu regex yang benar bisa mendeteksi attack dalam mikrodetik; satu regex yang salah bisa menghabiskan CPU 100% (ReDoS) dan membawa down seluruh sistem. Buat security engineer: (1) Paham engine β€” PCRE untuk rule yang butuh backreference, RE2 untuk log parsing performa tinggi. (2) Hindari nested quantifiers β€” (a+)+ adalah pola klasik ReDoS. Pake atomic group (?>...) atau possessive ++ untuk cegah backtracking gak perlu. (3) Test dengan input batas β€” regex yang OK buat 10 karakter bisa mati di 100 karakter. (4) Sigma rules adalah format universal detection β€” tulis sekali, deploy ke SIEM mana aja. (5) YARA rules untuk malware β€” tapi pastikan rule gak terlalu generic (false positive) atau terlalu spesifik (mudah di-evade). Investasi belajar regex adalah investasi yang membayar setiap hari β€” karena hampir SEMUA yang lo lakukan sebagai security engineer melibatkan pattern matching.