Cyber RoadmapINDONESIA

Fondasi · Fase 1

Membuat Tools Keamanan Sendiri dengan Python: Port Scanner, Log Parser, dan File Encryptor

Penutup modul scripting: membuat tiga alat keamanan sendiri dengan Python. Port scanner TCP dengan socket dan kerja paralel, log parser yang menghasilkan laporan CSV, dan file encryptor berbasis PBKDF2 dan Fernet, lengkap dengan aturan main dan alasan di balik setiap keputusan desain.

Ini materi penutup modul Python dan Bash. Sebelumnya kamu sudah belajar dasar Python, regex, requests, dan Bash. Sekarang semua itu dipakai untuk membuat tiga alat keamanan sungguhan:

  1. Port scanner, untuk mengetahui layanan apa saja yang terbuka di sebuah perangkat.
  2. Log parser, untuk meringkas ribuan baris log menjadi laporan yang bisa dibuka di spreadsheet.
  3. File encryptor, untuk melindungi file rahasia dengan password.

Alat profesional seperti nmap atau GPG tentu jauh lebih lengkap. Tujuan membuatnya sendiri bukan untuk menggantikan alat-alat itu, melainkan supaya kamu paham cara kerjanya dari dalam. Orang yang pernah membuat port scanner sendiri akan jauh lebih mudah memahami hasil nmap, dan orang yang pernah membuat alat enkripsi sendiri akan tahu kenapa password lemah tetap berbahaya walaupun filenya dienkripsi.

Persiapan

mkdir tools-keamanan && cd tools-keamanan
python3 -m venv venv
source venv/bin/activate
pip install cryptography        # hanya dibutuhkan oleh file encryptor

Ketiga alat ini memakai modul argparse, modul bawaan Python untuk membaca pilihan dari terminal, seperti -p 22,80 atau --output. Dengan argparse, skripmu otomatis punya petunjuk pemakaian lewat --help, persis seperti perintah Linux pada umumnya.

Alat 1: port scanner

Di artikel TCP, UDP, dan ICMP, kamu sudah belajar three-way handshake. Port scanner paling sederhana bekerja dengan memanfaatkannya: mencoba membuka koneksi TCP ke setiap port, lalu melihat jawabannya.

Tiga kemungkinan hasil pemindaian port: terbuka dijawab SYN-ACK, tertutup dijawab RST, dan difilter tanpa jawaban
Tiga kemungkinan hasil pemindaian port: terbuka dijawab SYN-ACK, tertutup dijawab RST, dan difilter tanpa jawaban
# port_scanner.py - pemindai port TCP sederhana, hanya untuk perangkat milikmu sendiri
import argparse
import socket
from concurrent.futures import ThreadPoolExecutor

PORT_UMUM = {21: "FTP", 22: "SSH", 25: "SMTP", 53: "DNS", 80: "HTTP",
             443: "HTTPS", 3306: "MySQL", 3389: "RDP", 8080: "HTTP-alt"}


def cek_port(host, port, timeout):
    """Mengembalikan port kalau terbuka, atau None kalau tertutup."""
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.settimeout(timeout)
        if s.connect_ex((host, port)) == 0:   # 0 berarti koneksi TCP berhasil
            return port
    return None


def urai_port(teks):
    """Mengubah '22,80,8000-8010' menjadi daftar angka port."""
    hasil = []
    for bagian in teks.split(","):
        if "-" in bagian:
            awal, akhir = bagian.split("-")
            hasil.extend(range(int(awal), int(akhir) + 1))
        else:
            hasil.append(int(bagian))
    return hasil


def main():
    p = argparse.ArgumentParser(description="Pemindai port TCP sederhana")
    p.add_argument("host", nargs="?", default="127.0.0.1", help="alamat target (bawaan: 127.0.0.1)")
    p.add_argument("-p", "--ports", default="1-1024", help="contoh: 22,80,443 atau 1-1024")
    p.add_argument("-t", "--timeout", type=float, default=0.5, help="detik per port")
    args = p.parse_args()

    ip = socket.gethostbyname(args.host)
    ports = urai_port(args.ports)
    print(f"Memindai {len(ports)} port di {args.host} ({ip}) ...")

    with ThreadPoolExecutor(max_workers=100) as kerja:
        hasil = kerja.map(lambda port: cek_port(ip, port, args.timeout), ports)
        terbuka = sorted(port for port in hasil if port)

    for port in terbuka:
        print(f"  {port:>5}/tcp  terbuka  {PORT_UMUM.get(port, '')}")
    print(f"Selesai: {len(terbuka)} port terbuka.")


if __name__ == "__main__":
    main()

Cara kerjanya:

  • Modul socket membuat sambungan jaringan. Fungsi connect_ex() mencoba terhubung dan mengembalikan 0 kalau berhasil, artinya port terbuka.
  • settimeout() membatasi waktu tunggu. Tanpa ini, port yang difilter firewall akan membuat pemindai menunggu sangat lama.
  • ThreadPoolExecutor memeriksa banyak port sekaligus, bukan satu per satu. Memindai 1.024 port satu per satu dengan timeout 0,5 detik bisa memakan waktu lebih dari 8 menit. Dengan 100 pekerja paralel, cukup beberapa detik.

Teknik ini disebut TCP connect scan, sama dengan nmap -sT. Karena handshake-nya diselesaikan sampai tuntas, setiap percobaan biasanya tercatat di log target. Itulah sebabnya pemindaian seperti ini mudah terdeteksi oleh tim keamanan.

Alat 2: log parser dengan laporan CSV

Di artikel String dan Regex untuk Parsing Log, kita membuat pengurai log SSH yang menampilkan hasilnya di layar. Sekarang kita tingkatkan menjadi alat yang lebih berguna untuk pekerjaan nyata: hasilnya disimpan sebagai file CSV yang bisa dibuka di Excel atau Google Sheets, lengkap dengan waktu pertama dan terakhir setiap IP mencoba masuk.

Baris-baris log SSH mentah diringkas menjadi tabel CSV berisi alamat IP, jumlah gagal, berhasil, jumlah akun, waktu, dan status
Baris-baris log SSH mentah diringkas menjadi tabel CSV berisi alamat IP, jumlah gagal, berhasil, jumlah akun, waktu, dan status
# log_parser.py - mengurai log SSH dan menyimpan ringkasan ke file CSV
import argparse
import csv
import re
from collections import defaultdict

POLA = re.compile(
    r"^(?P<waktu>\w{3}\s+\d+ [\d:]{8}) \S+ sshd\[\d+\]: "
    r"(?P<status>Failed|Accepted) password for (?:invalid user )?(?P<user>\S+) "
    r"from (?P<ip>\d{1,3}(?:\.\d{1,3}){3}) port \d+"
)


def urai(nama_file):
    data = defaultdict(lambda: {"gagal": 0, "berhasil": 0, "akun": set(), "pertama": None, "terakhir": None})
    with open(nama_file, encoding="utf-8", errors="replace") as f:
        for baris in f:
            m = POLA.search(baris)
            if not m:
                continue
            d = data[m["ip"]]
            d["gagal" if m["status"] == "Failed" else "berhasil"] += 1
            d["akun"].add(m["user"])
            d["pertama"] = d["pertama"] or m["waktu"]
            d["terakhir"] = m["waktu"]
    return data


def main():
    p = argparse.ArgumentParser(description="Ringkasan login SSH per alamat IP")
    p.add_argument("log", help="file log, misalnya /var/log/auth.log")
    p.add_argument("-o", "--output", default="ringkasan.csv", help="file CSV hasil")
    p.add_argument("-b", "--batas", type=int, default=5, help="batas login gagal untuk ditandai")
    args = p.parse_args()

    data = urai(args.log)
    with open(args.output, "w", newline="", encoding="utf-8") as f:
        tulis = csv.writer(f)
        tulis.writerow(["ip", "gagal", "berhasil", "jumlah_akun", "pertama", "terakhir", "status"])
        for ip, d in sorted(data.items(), key=lambda x: -x[1]["gagal"]):
            if d["gagal"] >= args.batas:
                status = "CURIGA"
            elif d["gagal"] and d["berhasil"]:
                status = "PERIKSA"
            else:
                status = "-"
            tulis.writerow([ip, d["gagal"], d["berhasil"], len(d["akun"]), d["pertama"], d["terakhir"], status])
            print(f"{ip:<15} gagal={d['gagal']:<3} berhasil={d['berhasil']}  akun={len(d['akun'])}  {status}")
    print(f"Ringkasan disimpan di {args.output}")


if __name__ == "__main__":
    main()

Hal baru di skrip ini:

  • defaultdict otomatis membuat data kosong untuk alamat IP yang baru pertama kali muncul, sehingga kita tidak perlu memeriksanya dulu.
  • set() menyimpan nama akun tanpa duplikat. Jumlah akun yang berbeda adalah petunjuk penting: satu IP yang mencoba banyak nama akun hampir pasti sedang menebak-nebak.
  • Modul csv menulis tabel dengan format yang benar, termasuk menangani tanda koma di dalam data.
  • Status PERIKSA menandai IP yang pernah gagal lalu berhasil masuk, pola yang kita temukan di artikel sebelumnya.

Alat 3: file encryptor

Enkripsi menjaga kerahasiaan (confidentiality), huruf C pada CIA Triad. Alat ini mengenkripsi satu file dengan password, sehingga isinya hanya bisa dibaca oleh orang yang tahu password tersebut.

Ada satu aturan emas dalam kriptografi: jangan pernah membuat algoritma enkripsi sendiri. Algoritma buatan sendiri hampir pasti punya celah. Karena itu kita memakai pustaka cryptography yang sudah diuji banyak ahli, dan bagian kita hanya merangkainya dengan benar.

Password dan salt diolah PBKDF2 sebanyak 600.000 kali menjadi kunci 32 byte, lalu dipakai Fernet untuk mengenkripsi file asli
Password dan salt diolah PBKDF2 sebanyak 600.000 kali menjadi kunci 32 byte, lalu dipakai Fernet untuk mengenkripsi file asli
# file_encryptor.py - mengenkripsi dan mendekripsi satu file dengan password
import argparse
import base64
import getpass
import os
import sys

from cryptography.fernet import Fernet, InvalidToken
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

PUTARAN = 600_000   # jumlah pengulangan agar password sulit ditebak massal


def buat_kunci(password, salt):
    """Mengubah password menjadi kunci enkripsi yang kuat."""
    kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=salt, iterations=PUTARAN)
    return base64.urlsafe_b64encode(kdf.derive(password.encode()))


def enkripsi(path, password):
    salt = os.urandom(16)                      # acak, berbeda setiap kali
    with open(path, "rb") as f:
        data = f.read()
    token = Fernet(buat_kunci(password, salt)).encrypt(data)
    with open(path + ".enc", "wb") as f:
        f.write(salt + token)                  # salt disimpan di depan, tidak rahasia
    print(f"Terenkripsi: {path}.enc (file asli tidak diubah)")


def dekripsi(path, password):
    with open(path, "rb") as f:
        isi = f.read()
    salt, token = isi[:16], isi[16:]
    try:
        data = Fernet(buat_kunci(password, salt)).decrypt(token)
    except InvalidToken:
        sys.exit("Gagal: password salah atau file sudah diubah.")
    keluaran = path.removesuffix(".enc") + ".dec"
    with open(keluaran, "wb") as f:
        f.write(data)
    print(f"Terdekripsi: {keluaran}")


def main():
    p = argparse.ArgumentParser(description="Enkripsi dan dekripsi satu file dengan password")
    p.add_argument("aksi", choices=["enkripsi", "dekripsi"])
    p.add_argument("file")
    args = p.parse_args()

    password = getpass.getpass("Password: ")   # tidak tampil di layar
    if args.aksi == "enkripsi":
        if password != getpass.getpass("Ulangi password: "):
            sys.exit("Password tidak sama.")
        enkripsi(args.file, password)
    else:
        dekripsi(args.file, password)


if __name__ == "__main__":
    main()
Komponen kriptografi di file encryptor
KomponenFungsinyaKenapa penting
PBKDF2Mengubah password menjadi kunci 32 byteDiulang 600.000 kali sehingga setiap tebakan password jadi lambat dan mahal bagi penyerang
Salt16 byte acak yang dicampur ke passwordPassword yang sama menghasilkan kunci berbeda di setiap file, sehingga penyerang tidak bisa memakai tabel tebakan siap pakai
FernetMengenkripsi data dengan AES dan melindunginya dengan HMACMenjaga kerahasiaan sekaligus keutuhan: file yang diubah satu byte saja akan ditolak saat didekripsi
getpassMeminta password tanpa menampilkannya di layarPassword tidak terlihat orang di sebelahmu dan tidak tersimpan di riwayat terminal

Perhatikan dua keputusan desain yang disengaja. Pertama, alat ini tidak menghapus file asli, supaya kamu tidak kehilangan data kalau lupa password atau terjadi kesalahan. Kedua, alat ini hanya mengenkripsi satu file yang kamu pilih sendiri. Alat yang otomatis mengenkripsi banyak file lalu menghapus aslinya adalah ciri khas ransomware, dan bukan sesuatu yang kita buat di sini.

Ringkasan ketiga alat

Tiga alat, konsep yang dipakai, dan padanan profesionalnya
AlatModul utamaKonsep yang dipakaiAlat profesional
Port scannersocket, concurrent.futuresTCP handshake, timeout, kerja paralelnmap, masscan
Log parserre, csv, collectionsRegex, grup bernama, agregasi dataSIEM seperti Wazuh, Splunk, atau Elastic
File encryptorcryptography, getpassPenurunan kunci, salt, enkripsi terautentikasiGPG, VeraCrypt, 7-Zip dengan AES

Kenapa ini penting untuk keamanan siber

Membuat alat sendiri melatih kemampuan yang dibutuhkan di semua bidang keamanan: memahami protokol sampai level paket, mengolah data dalam jumlah besar, dan memakai kriptografi dengan benar. Di dunia kerja, kamu akan sering menemui situasi saat alat yang ada tidak pas dengan kebutuhan, misalnya format log khusus milik aplikasi kantor, atau pemeriksaan rutin yang ingin diotomatiskan. Di saat seperti itu, kemampuan menulis alat kecil sendiri menjadi pembeda.

Ringkasan

  • Port scanner memakai socket.connect_ex() untuk mencoba koneksi TCP, dengan timeout dan kerja paralel agar cepat.
  • Log parser memakai regex untuk mengurai log, lalu merangkumnya per IP dan menyimpannya sebagai CSV.
  • File encryptor memakai PBKDF2 dengan salt untuk membuat kunci dari password, lalu Fernet untuk mengenkripsi dan melindungi keutuhan file.
  • argparse membuat skrip bisa dipakai seperti perintah Linux, lengkap dengan --help.
  • Pindai hanya perangkat milikmu sendiri, jangan membuat algoritma enkripsi sendiri, dan pakai password yang kuat.

Tonton dan baca juga

Dengan materi ini, kamu menyelesaikan modul Python dan Bash Scripting. Modul berikutnya kembali ke konsep inti keamanan, dimulai dengan CIA Triad yang dibahas lebih dalam lewat kasus-kasus nyata di Indonesia. Buka di roadmap untuk melanjutkan.

Bermanfaat? Dukung konten berikutnya

Semua materi di sini gratis. Dukunganmu dipakai untuk membeli alat lab untuk praktik, eksplorasi, dan riset konten keamanan siber berikutnya.

Traktir di Trakteer ↗
Sampul course Cyber Security Fundamental

Pelajari lebih dalam

Cyber Security Fundamental di WPU Course

Lihat course ↗

Materi terkait di roadmap

Artikel lainnya

Artikel ini bersifat edukatif. Praktikkan teknik ofensif hanya di lingkungan yang kamu miliki izinnya. Baca Disclaimer.