Fondasi · Fase 1
String Manipulation dan Regex Dasar untuk Parsing Log dengan Python
Log asli tidak serapi contoh di buku. Pelajari fungsi string Python, dasar regex, grup bernama, dan modul re, lalu terapkan untuk mengurai log SSH dari /var/log/auth.log: menghitung login gagal, menemukan akun palsu, dan mendeteksi login berhasil setelah gagal.

Di artikel Dasar Python untuk Keamanan Siber, kita membuat skrip penghitung login gagal dari log yang formatnya sengaja dibuat rapi. Di dunia nyata, log tidak serapi itu. Satu baris bisa berisi tanggal, nama mesin, nama program, nomor proses, lalu pesan yang susunannya berbeda-beda.
Untuk mengurai log seperti itu, kamu butuh dua keterampilan: manipulasi string, yaitu mengolah teks dengan fungsi-fungsi bawaan Python, dan regex (regular expression), yaitu bahasa kecil untuk mencari pola di dalam teks. Keduanya dipakai setiap hari oleh analis SOC, penanggap insiden, dan siapa pun yang bekerja dengan log.
Mengenal log SSH yang asli
Di Ubuntu dan Debian, catatan login SSH tersimpan di /var/log/auth.log. Di keluarga Red Hat, lokasinya /var/log/secure. Di sistem yang hanya memakai journal, kamu bisa melihatnya dengan journalctl -u ssh, seperti di artikel Proses di Linux.

Ada satu detail penting. Kalau penyerang mencoba nama akun yang tidak ada di server, log menambahkan kata invalid user sebelum namanya. Ini petunjuk berharga, karena orang yang sah jarang salah mengetik nama akunnya sendiri, sedangkan penyerang biasanya menebak nama-nama umum seperti admin, test, atau oracle.
Manipulasi string: alat dasar mengolah teks
Sebelum memakai regex, kenali dulu fungsi string bawaan. Untuk banyak tugas sederhana, fungsi-fungsi ini sudah cukup dan lebih mudah dibaca.
baris = " Nov 5 08:01:12 lab sshd[1201]: Failed password for budi from 203.0.113.7 port 52344 ssh2\n"
bersih = baris.strip() # buang spasi dan baris baru di awal dan akhir
print(bersih.startswith("Nov")) # True
print("Failed" in bersih) # True
print(bersih[:15]) # Nov 5 08:01:12 (15 karakter pertama)
bagian = bersih.split() # pecah berdasarkan spasi
print(bagian[8], bagian[10]) # budi 203.0.113.7
print(bersih.replace("budi", "[disensor]"))
# Nov 5 08:01:12 lab sshd[1201]: Failed password for [disensor] from 203.0.113.7 port 52344 ssh2
print(" | ".join(["203.0.113.7", "5 kali", "BLOKIR"]))
# 203.0.113.7 | 5 kali | BLOKIR
| Fungsi | Kegunaan | Contoh |
|---|---|---|
strip() | Membuang spasi dan baris baru di tepi | baris.strip() |
lower(), upper() | Menyamakan huruf besar dan kecil sebelum dibandingkan | baris.lower() |
split() | Memecah teks menjadi list | baris.split(":") |
startswith(), endswith() | Memeriksa awal atau akhir teks | nama.endswith(".log") |
in | Memeriksa apakah sebuah kata ada di dalam teks | "Failed" in baris |
replace() | Mengganti sebagian teks | baris.replace("budi", "***") |
teks[a:b] | Mengambil potongan teks (slicing) | baris[:15] |
"pemisah".join(list) | Menggabungkan list menjadi teks | ", ".join(akun) |
Kapan split() tidak cukup
Masalah muncul ketika susunan kata berubah. Perhatikan baris dengan invalid user:
baris = "Nov 5 08:03:01 lab sshd[1222]: Failed password for invalid user admin from 203.0.113.7 port 52401 ssh2"
print(baris.split()[8]) # invalid (bukan nama pengguna!)
Karena ada dua kata tambahan, posisi nama akun bergeser. Cara split() yang bergantung pada nomor posisi langsung salah. Di sinilah regex lebih unggul: ia mencari pola, bukan posisi.
Regex: mencari pola, bukan posisi
Regex adalah susunan simbol yang menggambarkan bentuk teks yang dicari. Misalnya, "satu atau lebih angka, lalu titik, diulang empat kali" adalah gambaran sebuah alamat IPv4.

Di Python, regex ada di modul bawaan re. Fungsi yang paling sering dipakai adalah re.search(), yang mencari kecocokan pertama di sebuah teks.
import re
baris = "Nov 5 08:03:01 lab sshd[1222]: Failed password for invalid user admin from 203.0.113.7 port 52401 ssh2"
cocok = re.search(r"from (\d+\.\d+\.\d+\.\d+) port (\d+)", baris)
if cocok:
print(cocok.group(1)) # 203.0.113.7
print(cocok.group(2)) # 52401
Tanda kurung ( ) membuat grup. Setiap grup menangkap bagian teks yang cocok, lalu bisa diambil dengan group(1), group(2), dan seterusnya. Titik ditulis \. karena titik biasa berarti "karakter apa saja".
Fungsi penting lainnya:
teks = "Serangan dari 203.0.113.7 dan 198.51.100.4, lalu 203.0.113.7 lagi."
pola_ip = r"\d{1,3}(?:\.\d{1,3}){3}"
print(re.findall(pola_ip, teks))
# ['203.0.113.7', '198.51.100.4', '203.0.113.7']
print(sorted(set(re.findall(pola_ip, teks)))) # tanpa duplikat
# ['198.51.100.4', '203.0.113.7']
print(re.sub(pola_ip, "x.x.x.x", teks)) # menyamarkan IP
# Serangan dari x.x.x.x dan x.x.x.x, lalu x.x.x.x lagi.
| Fungsi | Hasilnya | Cocok untuk |
|---|---|---|
re.search(pola, teks) | Kecocokan pertama, atau None | Mengambil data dari satu baris |
re.findall(pola, teks) | List semua kecocokan | Mengumpulkan semua IP di sebuah teks |
re.sub(pola, ganti, teks) | Teks yang sudah diganti | Menyamarkan data pribadi sebelum log dibagikan |
re.compile(pola) | Pola yang siap dipakai berulang | Mengurai ribuan baris dengan pola yang sama |
Grup bernama: hasil yang mudah dibaca
Kalau grupnya banyak, group(1), group(2), dan group(3) mudah tertukar. Python mendukung grup bernama dengan tulisan (?P<nama>...). Hasilnya bisa diambil sebagai dictionary.

Proyek: menganalisis log SSH
Sekarang kita buat pengurai log SSH yang sungguhan. Simpan contoh log berikut sebagai auth.log. Isinya meniru format asli Ubuntu, termasuk baris dari program lain (CRON) yang harus diabaikan.
Nov 5 08:01:12 lab sshd[1201]: Failed password for budi from 203.0.113.7 port 52344 ssh2
Nov 5 08:01:15 lab sshd[1201]: Failed password for budi from 203.0.113.7 port 52344 ssh2
Nov 5 08:02:40 lab sshd[1210]: Accepted password for sari from 198.51.100.4 port 50122 ssh2
Nov 5 08:03:01 lab sshd[1222]: Failed password for invalid user admin from 203.0.113.7 port 52401 ssh2
Nov 5 08:03:05 lab sshd[1222]: Failed password for invalid user oracle from 203.0.113.7 port 52401 ssh2
Nov 5 08:03:09 lab sshd[1225]: Failed password for root from 203.0.113.7 port 52455 ssh2
Nov 5 08:04:00 lab CRON[1230]: pam_unix(cron:session): session opened for user root by (uid=0)
Nov 5 08:06:30 lab sshd[1240]: Failed password for budi from 192.0.2.50 port 41000 ssh2
Nov 5 08:06:35 lab sshd[1240]: Failed password for budi from 192.0.2.50 port 41000 ssh2
Nov 5 08:06:40 lab sshd[1240]: Failed password for budi from 192.0.2.50 port 41000 ssh2
Nov 5 08:07:02 lab sshd[1244]: Accepted password for budi from 192.0.2.50 port 41022 ssh2
Lalu buat file parse_auth.py:
# parse_auth.py - mengurai log SSH dan mencari pola mencurigakan
import re
import sys
from collections import Counter
POLA = re.compile(
r"(?P<status>Failed|Accepted) password for "
r"(?P<invalid>invalid user )?(?P<user>\S+) "
r"from (?P<ip>\d{1,3}(?:\.\d{1,3}){3}) port (?P<port>\d+)"
)
def urai_log(nama_file):
"""Mengambil data penting dari setiap baris login SSH."""
kejadian = []
with open(nama_file, "r", encoding="utf-8", errors="replace") as f:
for baris in f:
cocok = POLA.search(baris)
if cocok:
kejadian.append(cocok.groupdict())
return kejadian
def analisis(kejadian):
gagal_ip = Counter()
gagal_user = Counter()
akun_palsu = set()
berhasil_setelah_gagal = []
for k in kejadian:
if k["status"] == "Failed":
gagal_ip[k["ip"]] += 1
gagal_user[k["user"]] += 1
if k["invalid"]:
akun_palsu.add(k["user"])
elif gagal_ip[k["ip"]] > 0:
berhasil_setelah_gagal.append(f'{k["user"]} dari {k["ip"]}')
print("Login gagal per IP:")
for ip, n in gagal_ip.most_common():
print(f" {ip:<15} {n} kali")
print("Akun yang paling sering dicoba:", ", ".join(u for u, _ in gagal_user.most_common(3)))
print("Akun yang tidak ada di sistem:", ", ".join(sorted(akun_palsu)) or "-")
for item in berhasil_setelah_gagal:
print(f"PERHATIAN: berhasil login setelah gagal -> {item}")
if __name__ == "__main__":
analisis(urai_log(sys.argv[1] if len(sys.argv) > 1 else "auth.log"))
Perhatikan pola regex di bagian atas. Bagian (?P<invalid>invalid user )? diakhiri tanda tanya, artinya boleh ada boleh tidak. Dengan satu pola saja, baris biasa dan baris invalid user sama-sama terurai dengan benar, masalah yang tidak bisa diatasi split(). Baris CRON tidak cocok dengan pola, sehingga otomatis terlewati.
Opsi errors="replace" saat membuka file membuat skrip tidak berhenti kalau ada karakter aneh di log, sesuatu yang cukup sering terjadi karena penyerang kadang sengaja mengirim data acak.
Bonus: regex di terminal
Regex tidak hanya ada di Python. Perintah grep yang sudah kamu kenal di artikel Navigasi Terminal Linux juga mendukungnya. Untuk analisis cepat tanpa menulis skrip, gabungan perintah ini sangat berguna:
grep "Failed password" auth.log | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | sort | uniq -c | sort -rn
# 5 203.0.113.7
# 3 192.0.2.50
Opsi -E mengaktifkan regex versi lengkap, dan -o hanya menampilkan bagian yang cocok. Lalu sort, uniq -c, dan sort -rn menghitung dan mengurutkannya. Hasilnya sama dengan skrip Python kita, dalam satu baris. Kapan pakai yang mana? Terminal cocok untuk pemeriksaan cepat. Python cocok kalau logikanya mulai rumit, seperti mendeteksi login berhasil setelah gagal.
Kebiasaan baik saat mengurai log
- Uji regex dengan contoh nyata sebelum dipakai ke log besar. Situs seperti regex101.com membantu melihat bagian mana yang cocok. Jangan menempelkan log asli yang berisi data sensitif ke situs pihak ketiga, pakai contoh yang sudah disamarkan.
- Buat pola sespesifik mungkin. Sertakan kata penanda seperti
fromdanportagar tidak salah tangkap. - Anggap isi log tidak bisa dipercaya. Nama pengguna di log ditulis oleh siapa pun yang mencoba login, termasuk penyerang. Jangan pernah menjalankan isi log sebagai perintah.
- Samarkan data pribadi dengan
re.sub()sebelum membagikan potongan log ke orang lain.
Kenapa ini penting untuk keamanan siber
Hampir semua jejak serangan tersimpan sebagai teks: log server, log firewall, log aplikasi web, dan catatan sistem deteksi. Alat besar seperti SIEM juga bekerja dengan mengurai log memakai pola seperti regex. Kalau kamu bisa membuat pola sendiri, kamu bisa mengolah log apa pun, menulis aturan deteksi, dan menemukan pola serangan yang tidak terlihat kalau dibaca satu per satu.
Ringkasan
- Fungsi string seperti
strip(),split(),replace(), danincukup untuk teks yang formatnya tetap. - Kalau susunan teks bisa berubah, gunakan regex yang mencari pola, bukan posisi.
- Simbol dasar regex:
\d,\w,\s,\S,+,*,?,{n,m},[ ],^,$, dan grup( ). - Modul
remenyediakansearch(),findall(),sub(), dancompile(). - Grup bernama
(?P<nama>...)membuat hasil mudah dipakai lewatgroupdict(). grep -oEmembawa kekuatan regex ke terminal untuk analisis cepat.
Tonton dan baca juga
- Video: Parsing file log pakai regex
- Artikel: Mengenal regex
- Artikel: Manipulasi string di Bash (bahasa Inggris)
Materi berikutnya membawa Python keluar dari komputermu: memakai pustaka requests untuk berkomunikasi dengan API dan mengambil data dari situs web. Buka di roadmap untuk melanjutkan.