Threat modeling adalah proses sistematis mengidentifikasi ancaman terhadap sistem sebelum kode ditulis. Tanpa threat modeling, programmer sering menambahkan kontrol keamanan reaktif setelah insiden—pendekatan yang mahal dan tidak lengkap. Microsoft memperkenalkan metodologi STRIDE pada 1999 untuk memandu threat modeling terstruktur. Pada chapter ini kita mendemonstrasikan STRIDE pada modul autentikasi sederhana, menunjukkan bagaimana setiap kategori ancaman memiliki kontrol spesifik yang dapat diimplementasikan dalam kode C.

Metodologi STRIDE #

STRIDE adalah akronim untuk enam kategori ancaman:

HurufKategoriContohKontrol
SSpoofingPenyerang menyamar sebagai pengguna sahAutentikasi kuat, MFA
TTamperingModifikasi data transit atau penyimpananHMAC, signature, integrity check
RRepudiationPengguna menyangkal melakukan aksiAudit log yang tidak dapat diubah
IInformation DisclosureKebocoran data sensitifEnkripsi, permission ketat
DDenial of ServiceSistem tidak tersediaRate limiting, quota
EElevation of PrivilegePengguna biasa memperoleh hak adminRBAC, least privilege

Setiap komponen sistem dianalisis terhadap keenam kategori. Mari kita lihat implementasinya pada modul autentikasi sederhana:

c
/* threat_modeling.c */
/* Demonstrasi prinsip STRIDE pada modul autentikasi sederhana. */
#include <stdio.h>
#include <string.h>
#include <stdint.h>

/* Pembandingan konstan-waktu untuk mencegah timing attack
   (S: Spoofing, bagian dari STRIDE). */
static int constant_time_equal(const char *a, const char *b, size_t n) {
    volatile uint8_t diff = 0;
    for (size_t i = 0; i < n; i++) {
        diff |= (uint8_t)a[i] ^ (uint8_t)b[i];
    }
    return diff == 0;
}

/* Autentikasi dummy — selalu bandingkan panjang sama untuk hindari leak. */
static int authenticate(const char *user, const char *token) {
    /* Di produksi: token disimpan sebagai hash (Argon2/bcrypt), bukan plain. */
    const char *expected_token = "T0pSecret123"; /* 12 char */
    const size_t expected_len = 12;

    if (user == NULL || token == NULL) return 0;
    if (strcmp(user, "admin") != 0) return 0;
    if (strlen(token) != expected_len) return 0;

    return constant_time_equal(token, expected_token, expected_len);
}

/* Audit log sederhana untuk anti-repudiation (R: Repudiation). */
static void audit_log(const char *event, int success) {
    /* Di produksi: tulis ke append-only log dengan timestamp monoton. */
    printf("[AUDIT] event='%s' result=%s\n",
           event, success ? "SUCCESS" : "FAILURE");
}

int main(void) {
    printf("=== Demonstrasi STRIDE pada modul autentikasi ===\n\n");

    /* S - Spoofing: cegah dengan pembandingan konstan-waktu. */
    printf("--- S: Spoofing ---\n");
    int ok1 = authenticate("admin", "T0pSecret123");
    int ok2 = authenticate("admin", "T0pSecret124");
    audit_log("login admin", ok1);
    audit_log("login admin (token salah)", ok2);

    /* T - Tampering: di produksi gunakan HMAC pada data sensitif. */
    printf("\n--- T: Tampering ---\n");
    printf("Konseptual: tanda tangan HMAC-SHA256 melindungi integritas data.\n");

    /* R - Repudiation: catat semua aksi penting. */
    printf("\n--- R: Repudiation ---\n");
    printf("Setiap aksi login/delete dicatat di audit log (lihat baris [AUDIT] di atas).\n");

    /* I - Information Disclosure: cegah leak via pesan error. */
    printf("\n--- I: Information Disclosure ---\n");
    int ok3 = authenticate("hacker", "guessing123");
    audit_log("login user tidak dikenal", ok3);
    printf("Pesan yang sama ('kredensial salah') untuk user tak dikenal/salah password.\n");

    /* D - Denial of Service: rate limiting. */
    printf("\n--- D: Denial of Service ---\n");
    printf("Konseptual: batasi 5 percobaan login per menit per IP.\n");

    /* E - Elevation of Privilege: cek role eksplisit. */
    printf("\n--- E: Elevation of Privilege ---\n");
    printf("Konseptual: tiap aksi admin wajib verifikasi role=='admin' terlebih dahulu.\n");

    printf("\n=== Ringkasan STRIDE ===\n");
    printf("S - Spoofing               : constant-time compare.\n");
    printf("T - Tampering              : HMAC/signature.\n");
    printf("R - Repudiation            : audit log.\n");
    printf("I - Information Disclosure  : pesan error generik.\n");
    printf("D - Denial of Service      : rate limiting.\n");
    printf("E - Elevation of Privilege : role-based access control.\n");
    return 0;
}

Output yang Dihasilkan:

text
=== Demonstrasi STRIDE pada modul autentikasi ===

--- S: Spoofing ---
[AUDIT] event='login admin' result=SUCCESS
[AUDIT] event='login admin (token salah)' result=FAILURE

--- T: Tampering ---
Konseptual: tanda tangan HMAC-SHA256 melindungi integritas data.

--- R: Repudiation ---
Setiap aksi login/delete dicatat di audit log (lihat baris [AUDIT] di atas).

--- I: Information Disclosure ---
[AUDIT] event='login user tidak dikenal' result=FAILURE
Pesan yang sama ('kredensial salah') untuk user tak dikenal/salah password.

--- D: Denial of Service ---
Konseptual: batasi 5 percobaan login per menit per IP.

--- E: Elevation of Privilege ---
Konseptual: tiap aksi admin wajib verifikasi role=='admin' terlebih dahulu.

=== Ringkasan STRIDE ===
S - Spoofing               : constant-time compare.
T - Tampering              : HMAC/signature.
R - Repudiation            : audit log.
I - Information Disclosure  : pesan error generik.
D - Denial of Service      : rate limiting.
E - Elevation of Privilege : role-based access control.

Mari kita telaah baris-baris kode di atas:

  1. Baris 10-15 (constant_time_equal): Kontrol terhadap Spoofing. Pembandingan string biasa seperti strcmp kembali lebih cepat pada karakter pertama yang tidak cocok—penyerang dapat mengukur waktu respons untuk menebak karakter satu per satu (timing attack). Pembandingan konstan-waktu selalu menjalankan loop penuh untuk seluruh panjang yang ditentukan. Kata kunci volatile mencegah compiler mengoptimalkan loop menjadi short-circuit.
  2. Baris 18-26 (authenticate): Modul autentikasi memverifikasi token. Pemeriksaan strlen(token) != expected_len pertama menolak token dengan panjang salah sebelum pembandingan konstan-waktu—memastikan kita selalu membandingkan jumlah byte yang sama. Catatan: token T0pSecret123 di-hardcode hanya untuk demo. Produksi wajib menyimpan hash Argon2id dari token, bukan plaintext.
  3. Baris 29-32 (audit_log): Kontrol terhadap Repudiation. Setiap aksi penting dicatat dengan status sukses/gagal. Audit log produksi harus append-only (tidak dapat di-edit), dengan timestamp monoton, dan idealnya disimpan di mesin terpisah agar penyerang yang mengkompromikan server aplikasi tidak dapat menghapus jejak.
  4. Baris 39-44 (Skenario S): Dua percobaan login dengan token berbeda. Pembandingan konstan-waktu mengembalikan SUCCESS untuk token yang benar dan FAILURE untuk yang salah—keduanya membutuhkan waktu yang sama. Audit log mencatat kedua percobaan sehingga administrator dapat melihat pola serangan.
  5. Baris 51-53 (Skenario I): Pesan error generik untuk user tak dikenal maupun password salah. Bila program membalas “user tidak ditemukan” untuk nama pengguna yang tidak ada dan “password salah” untuk nama pengguna yang ada, penyerang dapat enumerate daftar username valid. Pesan yang sama untuk kedua kasus mencegah user enumeration attack.
  6. Baris 60-63 (Ringkasan STRIDE): Pemetaan eksplisit antara setiap kategori ancaman dan kontrol yang diterapkan. Ringkasan ini menjadi dokumen referensi untuk audit keamanan.

Demonstrasi Fail Secure pada Autentikasi #

Pola fail secure dapat diaplikasikan langsung pada modul autentikasi. Berikut implementasi yang menolak login pada setiap kondisi tak terduga:

c
/* extra_threat_modeling.c — fail secure pada error autentikasi. */
#include <stdio.h>
#include <string.h>

/* Simulasi autentikasi dengan fail secure. */
static int authenticate_fail_secure(const char *user, const char *token) {
    /* Bila ada kondisi tak terduga, kembalikan 0 (tolak). */
    if (user == NULL || token == NULL) return 0;
    if (strlen(user) == 0 || strlen(token) == 0) return 0;
    if (strlen(user) > 64 || strlen(token) > 256) return 0;

    /* Simulasi: anggap hanya "admin" yang valid. */
    if (strcmp(user, "admin") != 0) return 0;
    /* Bandingkan token — di produksi pakai hash Argon2id. */
    return strcmp(token, "secret123") == 0 ? 1 : 0;
}

int main(void) {
    struct { const char *user; const char *token; } cases[] = {
        { "admin", "secret123" }, /* sukses */
        { "admin", "wrong" },     /* token salah */
        { NULL, "secret123" },    /* user NULL */
        { "admin", NULL },        /* token NULL */
        { "admin", "" },          /* token kosong */
    };
    const size_t n = sizeof(cases) / sizeof(cases[0]);
    for (size_t i = 0; i < n; i++) {
        int ok = authenticate_fail_secure(cases[i].user, cases[i].token);
        printf("user='%s' token='%s' -> %s\n",
               cases[i].user ? cases[i].user : "(null)",
               cases[i].token ? cases[i].token : "(null)",
               ok ? "IZINKAN" : "TOLAK (fail secure)");
    }
    return 0;
}

Output yang Dihasilkan:

text
user='admin' token='secret123' -> IZINKAN
user='admin' token='wrong' -> TOLAK (fail secure)
user='(null)' token='secret123' -> TOLAK (fail secure)
user='admin' token='(null)' -> TOLAK (fail secure)
user='admin' token='' -> TOLAK (fail secure)

Hanya kombinasi user dan token yang valid yang menghasilkan “IZINKAN”. Setiap kondisi tak terduga—pointer NULL, string kosong, panjang berlebihan—dikembalikan ke 0 (tolak). Bandingkan dengan pola fail open yang berbahaya: return (user && token && check_db(user, token));—bila check_db mengalami error dan kembali 0, && short-circuit tetap mengembalikan 0 (tolak), tetapi bila programmer lupa menambah && dan menulis return check_db(user, token);, error akan jadi pintu masuk.

Lebih Dalam: Setiap Kategori STRIDE #

S - Spoofing #

Spoofing terjadi ketika penyerang menyamar sebagai entitas sah. Bentuk umum: pencurian token sesi, serangan man-in-the-middle, falsifikasi alamat IP. Kontrol di C:

  • Pembandingan konstan-waktu untuk token: lihat constant_time_equal di atas.
  • TLS mutual authentication untuk komunikasi server-to-server: pakai OpenSSL dengan verifikasi client certificate.
  • MFA untuk pengguna manusia: implementasi TOTP/HOTP di C dengan pustaka seperti liboath.

T - Tampering #

Tampering adalah modifikasi data—baik di transit, di storage, atau di memori. Kontrol:

  • HMAC-SHA256 untuk integritas pesan: HMAC(key, data) menghasilkan tag 32 byte.
  • Digital signature untuk non-repudiation (siapa yang menandatangani tidak dapat menyangkal).
  • Memory protection via mprotect untuk region yang seharusnya read-only setelah inisialisasi.

R - Repudiation #

Repudiation adalah penyangkalan aksi. Bila sistem tidak dapat membuktikan siapa melakukan apa, akuntabilitas hilang. Kontrol:

  • Audit log append-only: gunakan file dengan flag O_APPEND sehingga data hanya dapat ditambah, tidak diubah.
  • Timestamp monoton: gunakan clock_gettime(CLOCK_MONOTONIC) atau NTP-signed timestamp.
  • Log forwarding: kirim log ke mesin terpisah secara real-time untuk mencegah penghapusan lokal.

I - Information Disclosure #

Kebocoran informasi terjadi melalui banyak vektor: pesan error, log, file permission longgar, side channel. Kontrol:

  • Pesan error generik: jangan bedakan “user tidak ada” vs “password salah”.
  • Filter data di log: jangan catat token, password, nomor kartu kredit utuh—mask atau hash.
  • File permission 0600: untuk file yang berisi data sensitif (lihat chapter Secure File Handling).
  • Memory zeroing: setelah memakai buffer yang berisi data sensitif, timpa dengan memset_s (C11 Annex K) sebelum free.

D - Denial of Service #

Denial of Service (DoS) membuat sistem tidak tersedia. Bentuk umum: flood koneksi, input besar yang membebani parser, deadlock. Kontrol:

  • Rate limiting: batasi permintaan per IP atau per pengguna.
  • Input size limit: tolak request yang lebih besar dari ambang batas sebelum parsing.
  • Timeout: batasi durasi koneksi tunggal—jangan biarkan koneksi idle tak terbatas.
  • Resource quota: batasi CPU, memori, dan disk per pengguna dengan setrlimit.

E - Elevation of Privilege #

Elevation of Privilege (EoP) terjadi ketika pengguna biasa memperoleh hak admin. Bentuk umum: eksploitasi bug buffer overflow untuk eksekusi kode sebagai root, insecure deserialization yang memuat objek privileged, path traversal ke file konfigurasi admin. Kontrol:

  • Drop privilege segera setelah startup: setuid(non_privileged_uid) setelah membuka port.
  • Role-based access control (RBAC): setiap aksi admin wajib verifikasi role == ADMIN sebelum eksekusi.
  • Capability bounding: gunakan capng untuk membatasi Linux capabilities pada binary yang membutuhkan beberapa privilege tapi tidak semua.

Diagram Alur Threat Modeling #

Threat modeling biasanya disajikan sebagai diagram data flow. Untuk modul autentikasi di atas, alurnya:

Diagram mengidentifikasi setiap titik di mana data mengalir dan menyematkan kontrol keamanan di setiap perbatasan.