Hardening auth untuk self-hosted Git Forge dan portal developer bukan sekadar menambahkan login dan MFA. Sistem seperti Git hosting internal, portal developer, atau platform kolaborasi repository biasanya menangani akun engineer, akses source code, webhook, token integrasi, dan invitation ke organisasi atau repository. Jika auth lemah, dampaknya bukan hanya pembajakan akun, tetapi juga kebocoran kode, supply-chain compromise, dan eskalasi akses ke sistem lain.

Untuk konteks self-hosted, tantangannya lebih besar: Anda tidak bisa mengandalkan proteksi default dari platform SaaS. Anda perlu mendesain sendiri bagaimana session dikelola, bagaimana token diterbitkan, bagaimana abuse dicegah pada signup dan invite flow, serta bagaimana event sensitif dicatat untuk audit. Artikel ini fokus pada pola implementasi yang aman dan realistis, bukan ulasan produk.

Threat model yang relevan untuk Git forge dan portal developer

Sebelum membahas kontrol, tentukan ancaman yang memang relevan. Aplikasi mirip Git forge dan developer portal biasanya memiliki permukaan serangan berikut:

  • Credential stuffing pada login web dan API.
  • Session hijacking melalui cookie yang bocor, XSS, atau transport yang tidak aman.
  • CSRF untuk aksi yang terlihat “internal”, seperti membuat access token, menerima invitation, atau menambah SSH key.
  • Password reset abuse untuk enumerasi akun atau takeover via token reset yang lemah.
  • Invitation abuse untuk spam, privilege escalation, atau bypass verifikasi email.
  • Signup abuse untuk pembuatan akun massal, trial abuse, atau penanaman akun sleeper.
  • MFA bypass karena implementasi challenge yang tidak mengikat state login.
  • Secret leakage dari environment variable, log, dump error, atau backup database.
  • Insufficient auditability sehingga insiden sulit ditelusuri.

Threat model ini menentukan prioritas. Untuk sistem internal dengan akses ke source code, baseline yang masuk akal adalah: cookie session aman untuk web, token terpisah untuk API/automation, MFA opsional atau wajib untuk role sensitif, rate limit berbasis identitas dan IP, serta audit log untuk semua event auth penting.

Memilih model auth: session cookie vs token

Pertanyaan pertama biasanya bukan “pakai JWT atau tidak”, tetapi siapa kliennya dan bagaimana interaksi dilakukan. Untuk Git forge dan portal developer, umumnya ada dua kategori:

  • Browser interaktif: dashboard, pengaturan akun, repository settings, invitation management.
  • API dan automation: CLI internal, CI/CD, webhook consumer, integrasi machine-to-machine.

Session cookie untuk browser

Untuk aplikasi web yang diakses melalui browser, session cookie server-side biasanya lebih aman dan lebih mudah di-hardening dibanding token bearer di browser. Alasannya:

  • Cookie bisa diberi atribut HttpOnly sehingga tidak bisa diakses JavaScript.
  • Cookie bisa dibatasi dengan Secure dan SameSite.
  • Session bisa dicabut dari server tanpa menunggu expiry token.
  • Rotasi session ID setelah login atau step-up auth lebih mudah dilakukan.

Untuk use case ini, simpan state session di server atau store terpusat seperti database/Redis, dan jadikan cookie hanya sebagai pointer ke session tersebut.

Token untuk API, CLI, dan automation

Token bearer tetap relevan untuk API dan automation karena non-browser client tidak cocok dengan model cookie + CSRF. Namun, jangan mencampur token browser dan token API tanpa alasan kuat. Pisahkan:

  • Session cookie untuk web UI.
  • Personal access token atau token terikat scope untuk CLI/API.
  • Token service account untuk automation yang bisa dirotasi terpisah dari user.

Untuk token API, minimalkan scope, beri expiry bila memungkinkan, hash token sebelum disimpan, dan tampilkan nilai token penuh hanya saat pembuatan.

Tabel trade-off

PendekatanCocok untukKelebihanKekurangan
Session cookie server-sideWeb UI browserRevocation mudah, HttpOnly, rotasi sederhanaPerlu proteksi CSRF, butuh session store
Stateless bearer tokenAPI, CLI, integrasiMudah dipakai non-browser clientRevocation lebih sulit jika tidak ada lookup server-side
Long-lived personal tokenDeveloper tooling lamaPraktis untuk integrasiRisiko tinggi jika bocor, sering lupa dirotasi
Short-lived scoped tokenAutomation modernBlast radius kecil, lebih amanFlow issuance lebih kompleks

Prinsip praktis: pakai cookie untuk browser, token untuk non-browser. Jangan menyimpan bearer token akses web di localStorage jika Anda bisa menggunakan session cookie yang benar.

Desain session yang aman: rotasi, expiry, dan pencabutan

Rotasi session setelah login dan event sensitif

Session fixation masih sering lolos review. Setelah user berhasil login, regenerasi session ID. Lakukan juga rotasi session ketika:

  • MFA baru saja diverifikasi.
  • Password diubah.
  • Email utama diubah.
  • Role sensitif berubah, misalnya menjadi admin organisasi.

Tujuannya agar identifier session lama tidak tetap valid setelah transisi state penting.

Absolute timeout dan idle timeout

Satu expiry saja biasanya tidak cukup. Gunakan kombinasi:

  • Idle timeout: session berakhir jika tidak aktif dalam rentang tertentu.
  • Absolute timeout: session tetap berakhir setelah umur maksimum, walau aktif terus.

Ini membatasi nilai token/cookie yang bocor. Untuk panel admin atau setting keamanan akun, Anda juga bisa menambahkan reauthentication window, misalnya minta password atau MFA lagi sebelum aksi sensitif seperti membuat access token baru.

Session metadata dan device management

Simpan metadata session minimal berikut:

  • user_id
  • session_id acak berentropi tinggi
  • created_at, last_seen_at, expires_at
  • ip terakhir atau ringkasan IP
  • user-agent ringkas
  • mfa_level atau authentication assurance level

Dengan metadata ini, Anda bisa menyediakan fitur “logout dari perangkat lain”, mendeteksi anomali, dan meningkatkan kualitas audit log. Jangan mengandalkan fingerprinting agresif sebagai faktor keamanan utama; itu sering rapuh dan menghasilkan false positive.

Contoh struktur session server-side

session = {
  id: random_opaque_id,
  user_id: 12345,
  created_at: "2026-06-09T10:00:00Z",
  last_seen_at: "2026-06-09T12:00:00Z",
  expires_at: "2026-06-10T10:00:00Z",
  auth_level: "password+mfa",
  csrf_secret: random_secret,
  ip_last: "203.0.113.10",
  user_agent: "Firefox on Linux"
}

Cookie berisi hanya session ID opaque, bukan data user atau role yang bisa dimanipulasi klien.

Proteksi CSRF yang benar untuk portal developer

Pada Git forge self-hosted, aksi sensitif bukan cuma “ubah profil”. Contoh endpoint yang wajib diperlakukan sensitif:

  • Membuat atau mencabut access token.
  • Menambah SSH key atau deploy key.
  • Menerima invitation ke repo atau organisasi.
  • Mengubah visibility repository.
  • Membuat webhook atau secret integrasi.
  • Menonaktifkan MFA.

Jika aplikasi memakai cookie untuk autentikasi browser, endpoint yang mengubah state harus dilindungi dari CSRF.

Pola yang umum dipakai

  • Synchronizer token: server menyimpan secret/token CSRF per session, lalu form mengirimkan nilainya kembali.
  • Double-submit cookie: cocok untuk arsitektur tertentu, tetapi tetap perlu implementasi hati-hati.
  • SameSite cookie: berguna sebagai lapisan tambahan, bukan pengganti penuh validasi CSRF.

Untuk UI tradisional server-rendered, synchronizer token per session biasanya paling jelas. Untuk SPA yang masih memakai cookie auth, tetap kirim token CSRF via header custom dan validasi di server.

Kesalahan yang sering terjadi

  • Menganggap SameSite saja sudah cukup.
  • Melindungi form HTML, tetapi lupa endpoint JSON internal.
  • Tidak mewajibkan POST/PUT/PATCH/DELETE untuk aksi mutasi.
  • Endpoint invitation accept memakai GET dan langsung mengubah state.

Gunakan GET hanya untuk menampilkan halaman konfirmasi. Eksekusi aksinya lewat POST dengan CSRF token valid.

Password, reset password, dan verifikasi email

Penyimpanan password

Password harus di-hash dengan algoritme password hashing modern yang memang dirancang lambat dan memakai salt unik per password. Jangan memakai hash umum cepat. Selain itu:

  • Pertimbangkan pepper terpisah jika arsitektur Anda mendukung pengelolaan secret yang baik.
  • Jangan log password mentah, bahkan pada mode debug.
  • Jangan memotong password secara diam-diam tanpa memberi tahu user.

Password reset yang aman

Reset password adalah target favorit attacker karena sering menjadi jalur bypass login utama. Pola yang lebih aman:

  1. User mengajukan reset dengan email atau username.
  2. Respons ke klien selalu generik: jangan ungkap apakah akun ada.
  3. Server membuat token acak sekali pakai berentropi tinggi dan menyimpan hash token, bukan token mentah.
  4. Token punya expiry pendek dan invalid setelah digunakan.
  5. Saat password berhasil direset, cabut session aktif lain dan pertimbangkan pencabutan token sensitif.
  6. Kirim notifikasi out-of-band bahwa password telah diubah.

Jangan gunakan token reset yang deterministik dari user ID, timestamp, atau hash email. Jangan menyimpan token reset mentah di database jika bisa dihindari.

Contoh alur reset password

POST /password-reset/request
- input: email_or_username
- response: "Jika akun ada, instruksi telah dikirim"
- server:
  - rate limit by IP + account key
  - generate random token
  - store hash(token), user_id, expires_at, used_at=null
  - send link with raw token via email

POST /password-reset/confirm
- input: token, new_password
- server:
  - hash(token), lookup active record
  - verify not expired and not used
  - set new password hash
  - mark token used
  - revoke other sessions
  - send security notification

Verifikasi email

Pada platform dengan invitation dan kolaborasi repository, email verification penting bukan hanya untuk onboarding, tetapi juga untuk integritas identitas. Gunakan verifikasi email untuk:

  • Mengaktifkan akun baru sepenuhnya.
  • Menyetujui invitation berdasarkan email.
  • Mengizinkan aksi tertentu seperti membuat organisasi baru atau menambahkan billing contact.

Trade-off utamanya adalah friction. Solusi praktis: izinkan eksplorasi terbatas sebelum verifikasi, tetapi blok aksi sensitif sampai email verified.

MFA/TOTP dan step-up authentication

Kapan MFA wajib?

Untuk self-hosted Git forge, MFA sebaiknya minimal diwajibkan pada:

  • Admin instance.
  • Owner organisasi.
  • User dengan akses ke repository sensitif atau production deployment config.

Untuk user lain, MFA bisa sangat dianjurkan dengan kebijakan bertahap. Jika organisasi memiliki kebijakan sendiri, enforce MFA di boundary organisasi, bukan hanya global.

Implementasi TOTP yang sering aman secara praktis

TOTP masih cukup relevan untuk self-hosted karena mudah diadopsi dan tidak bergantung pada konektivitas khusus. Praktik yang disarankan:

  • Simpan secret TOTP terenkripsi saat disimpan, bukan plaintext.
  • Tampilkan recovery codes sekali saat enrolment, simpan hanya bentuk hash bila memungkinkan.
  • Jangan menonaktifkan MFA hanya dengan session biasa; minta reauth.
  • Ikat hasil challenge MFA ke session baru atau session yang di-rotate.

Jika Anda mendukung metode MFA lain, seperti passkey atau WebAuthn, itu bisa lebih tahan phishing. Namun untuk banyak deployment internal, TOTP tetap baseline yang realistis.

Alur login dengan MFA

  1. User memasukkan identifier dan password.
  2. Jika password valid dan akun membutuhkan MFA, buat state login sementara dengan expiry pendek.
  3. User memasukkan kode TOTP.
  4. Setelah validasi sukses, rotate session ID dan tandai auth_level=password+mfa.
  5. Baru setelah itu izinkan akses ke area aplikasi.

Kesalahan umum: membuat session penuh sebelum MFA selesai, lalu hanya menambah flag di client. Ini membuka ruang bypass jika state transisi tidak ketat.

Rate limit login dan lockout yang tidak mudah di-abuse

Masalah dengan lockout klasik

Lockout permanen atau terlalu agresif sering berubah menjadi alat denial-of-service terhadap user sah. Attacker cukup mencoba banyak password salah pada username target, lalu akun korban terkunci.

Pendekatan yang lebih seimbang

Gunakan kombinasi kontrol berikut:

  • Rate limit per IP untuk membatasi volume kasar.
  • Rate limit per account identifier untuk mencegah brute force terarah.
  • Progressive backoff daripada lockout total permanen.
  • Risk signal tambahan untuk challenge ekstra jika pola mencurigakan.

Jika lockout dipakai, buatlah:

  • Durasi terbatas, bukan permanen.
  • Tidak mengungkap ke attacker apakah username valid.
  • Tidak mudah dipicu lintas banyak IP tanpa kontrol tambahan.

Contoh strategi sederhana

key dimensions:
- per_ip: 5-10 percobaan / window pendek
- per_account: backoff bertahap jika gagal berulang
- per_ip_account_pair: lebih ketat untuk kombinasi spesifik

on failed login:
- increment counters
- if threshold crossed: delay response or require CAPTCHA/challenge
- if repeated high-risk pattern: temporary lock on account actions, not global permanent disable

Jangan lupa mengatur rate limit pada endpoint berikut juga:

  • Password reset request
  • Email verification resend
  • MFA challenge submit
  • Invitation create / resend
  • Signup

Validasi input pada login, signup, dan invitation flow

Login

Field login terlihat sederhana, tetapi tetap perlu validasi defensif:

  • Normalisasi identifier bila memang desain Anda mengizinkan, misalnya trim whitespace.
  • Jangan mengandalkan validasi regex kaku yang mematahkan email valid.
  • Jangan bedakan pesan error “password salah” vs “user tidak ada”.
  • Waspadai log injection jika identifier masuk ke audit log atau reverse proxy log.

Signup

Signup untuk portal developer sering menjadi jalur abuse. Kontrol yang berguna:

  • Verifikasi email sebelum aktivasi penuh.
  • Rate limit per IP, subnet, dan fingerprint ringan bila perlu.
  • Deteksi disposable email jika sesuai kebijakan.
  • Moderasi atau approval untuk domain tertentu jika ini sistem enterprise.
  • Jangan auto-assign role tinggi hanya karena domain email cocok tanpa validasi tambahan.

Jika signup terbuka, pertimbangkan flow “akun dibuat tapi akses repository privat tetap butuh invitation eksplisit”. Ini membatasi blast radius akun baru.

Repository invitation

Invitation sering dianggap fitur kolaborasi biasa, padahal ia adalah boundary keamanan. Pastikan:

  • Invitation terikat ke email atau account target dengan jelas.
  • Role yang ditawarkan tervalidasi server-side, bukan hanya dari UI.
  • Invitation punya expiry dan bisa dicabut.
  • Menerima invitation membutuhkan autentikasi dan, bila perlu, email verified.
  • Owner tidak bisa mengundang melebihi kebijakan organisasi tanpa otorisasi.

Jika invitation dikirim ke email yang belum memiliki akun, jangan otomatis mengaktifkan akses penuh hanya dengan klik link. Idealnya: user menyelesaikan signup/verifikasi, lalu invitation diklaim secara eksplisit.

Pencegahan abuse pada invite

  • Rate limit jumlah invite per user, per organisasi, dan per waktu.
  • Batasi resend invitation.
  • Catat invitation burst yang tidak biasa.
  • Terapkan kebijakan domain allowlist untuk organisasi tertentu jika relevan.

Audit log yang benar-benar berguna

Audit log auth tidak boleh sekadar “user login”. Ia harus membantu investigasi dan mendukung deteksi insiden. Minimal catat event berikut:

  • Login sukses dan gagal
  • Logout
  • Password change
  • Password reset requested / completed
  • Email verification sent / completed
  • MFA enabled / disabled / challenged / failed
  • Session revoked
  • Token created / used / revoked
  • Invitation created / resent / accepted / revoked
  • Role change dan permission-sensitive action

Setiap event sebaiknya punya:

  • timestamp
  • actor_user_id atau anonymous context
  • target resource
  • result: success/failure
  • source IP
  • user-agent ringkas
  • request ID atau correlation ID

Hindari memasukkan secret, token mentah, kode MFA, atau password ke audit log. Audit log harus append-friendly dan sulit dimodifikasi diam-diam. Jika memungkinkan, kirim juga ke sistem log terpisah agar insiden pada aplikasi utama tidak sekaligus menghapus jejak.

Secret handling: env, secret manager, dan rotasi

Self-hosted deployment sering menyimpan terlalu banyak rahasia di file .env, lalu file itu ikut masuk backup, dump support, atau container image. Praktik yang lebih aman:

  • Simpan hanya secret yang memang perlu di environment runtime.
  • Gunakan secret manager atau mekanisme secret dari platform orkestrasi jika tersedia.
  • Pisahkan secret untuk signing session, encryption at rest, TOTP encryption, dan email provider.
  • Rencanakan rotasi key, bukan hanya penyimpanan awal.

Hal yang perlu dipisahkan

  • Session signing/encryption key
  • Password pepper jika digunakan
  • Email SMTP/API credentials
  • Database credentials
  • Token encryption keys
  • Webhook signing secrets

Kesalahan umum adalah memakai satu secret aplikasi untuk terlalu banyak fungsi. Jika satu key bocor, blast radius menjadi besar dan rotasi sulit dilakukan tanpa downtime atau invalidasi masif yang tak terencana.

Contoh alur end-to-end yang lebih aman

Alur signup dan invitation

  1. User menerima invitation ke email tertentu.
  2. User membuka halaman claim invitation; server belum mengubah membership.
  3. Jika belum punya akun, user signup dengan rate limit aktif.
  4. Sistem mengirim email verification.
  5. Setelah email verified dan login sukses, user men-submit accept invitation via POST dengan CSRF token.
  6. Server memverifikasi invitation masih aktif, role valid, org policy cocok, lalu membuat membership.
  7. Event audit dicatat untuk invitation accepted dan membership created.

Alur ini lebih aman daripada “klik link = langsung masuk repo”, karena ada autentikasi, verifikasi identitas, dan validasi state di sisi server.

Alur login web

  1. User mengirim username/email dan password.
  2. Server memeriksa rate limit.
  3. Jika valid, server membuat state sementara atau session transisi.
  4. Jika MFA diwajibkan, user menyelesaikan challenge TOTP.
  5. Session ID di-rotate dan cookie baru dikirim dengan atribut aman.
  6. Server mengembalikan halaman/app state tanpa mengekspos token bearer ke JavaScript.

Contoh checklist implementasi

Checklist auth inti

  • Cookie auth untuk web diberi HttpOnly, Secure, dan kebijakan SameSite yang sesuai.
  • Session ID opaque, acak, dan di-rotate setelah login/MFA/password change.
  • Ada idle timeout dan absolute timeout.
  • State-changing request terlindungi CSRF.
  • Password di-hash dengan algoritme password hashing modern.
  • Password reset memakai token acak sekali pakai yang disimpan dalam bentuk hash.
  • Email verification diwajibkan sebelum aksi sensitif tertentu.
  • MFA tersedia, dan diwajibkan untuk admin/owner.
  • Access token API dipisah dari session browser dan dibatasi scope.

Checklist abuse prevention

  • Rate limit login per IP, per akun, dan per pasangan IP-akun.
  • Signup dilindungi dari burst abuse dan email disposable sesuai kebijakan.
  • Password reset request dan resend verification ikut di-rate-limit.
  • Invitation create/resend/accept diawasi dan dibatasi.
  • Pesan error tidak mempermudah enumerasi akun.

Checklist observability dan operasional

  • Audit log tersedia untuk semua event auth penting.
  • Request ID/correlation ID tersimpan di log aplikasi dan reverse proxy.
  • Alert untuk lonjakan login gagal, reset request, dan invite burst.
  • Secret dipisah menurut fungsi dan punya prosedur rotasi.
  • Ada prosedur revoke session global saat insiden.

Kesalahan umum yang sering lolos review

  • Menyimpan token browser di localStorage padahal bisa memakai cookie HttpOnly.
  • Login sukses membuat session penuh sebelum MFA selesai.
  • Endpoint JSON sensitif lupa proteksi CSRF karena hanya form HTML yang diuji.
  • Password reset token disimpan plaintext di database.
  • Invitation accept via GET dan langsung memodifikasi membership.
  • Lockout terlalu agresif sehingga attacker bisa mengunci akun korban.
  • Pesan error berbeda antara user tidak ada dan password salah.
  • Token API tanpa scope atau tanpa jalur revocation yang jelas.
  • Secret bocor ke log saat debug email, webhook, atau auth middleware.
  • Audit log minim konteks sehingga tidak berguna saat investigasi.

Penutup

Hardening auth untuk self-hosted Git forge dan portal developer sebaiknya diperlakukan sebagai desain sistem, bukan fitur tambahan. Pilihan antara session cookie dan token harus mengikuti tipe klien. Session perlu rotasi dan expiry yang masuk akal. CSRF wajib untuk browser auth. Password reset, email verification, MFA, dan invitation flow harus dirancang dengan asumsi bahwa attacker akan mencari jalur termudah, bukan jalur resmi.

Jika Anda ingin baseline yang kuat, mulai dari kombinasi berikut: server-side session cookie untuk web, token scoped untuk API, CSRF protection, reset token sekali pakai, email verification, MFA untuk role sensitif, rate limit bertingkat, audit log lengkap, dan secret management yang bisa dirotasi. Itu bukan jaminan sempurna, tetapi cukup untuk menutup banyak celah yang paling sering dieksploitasi pada sistem self-hosted.