Mengimplementasikan MFA di Android


Saat ini, teknologi sudah menjangkau hampir segala hal. Mulai dari infak yang sudah bisa lewat dompet digital, hingga beli barang mahal bisa lewat e-commerce. Karena itulah, keamanan data pribadi menjadi semakin penting. Username/email dan password tidak lagi cukup untuk melindungi akun digital kita.

Pun meski password yang kita pakai sudah sangat susah ditebak, selalu ada celah -terutama di sisi manusianya- sedemikian sehingga akun kita masih rawan kebobolan. Sudah pernah baca tentang situs-situs yang kebobolan, kan? Kalau belum, coba sekali-kali kunjungi situs ini untuk mengetahui apakah email yang biasa kamu pakai untuk mendaftar ke berbagai situs sudah kebobolan atau belum.

Salah satu cara lebih memperkuat keamanan akun kita adalah dengan menggunakan metode 2 factor authentication alias 2FA, atau secara umum disebut Multi factor authentication (MFA). Beberapa situs belanja maupun aplikasi media sosial sudah menerapkan metode ini, biasanya lewat SMS. Jadi setelah seseorang login, dia akan diminta memasukkan kode yang dikirim ke nomor ponselnya.

Apakah metode ini sudah cukup aman? Sayangnya tidak. Sudah banyak kasus akun seseorang kebobolan meski sudah dilengkapi dengan 2FA lewat SMS.

Nah, salah satu cara mengamankan akun kita adalah dengan 2FA menggunakan aplikasi pihak ketiga, seperti Google Authenticator maupun aplikasi lain seperti Authy. Aplikasi semacam ini diklaim lebih aman dibanding hanya mengandalkan SMS saja.

Di tulisan ini, kita akan belajar membuat aplikasi Android yang menerapkan 2FA. Aplikasinya sederhana saja. Di screen pertama, kita akan membuat halaman untuk menggenerate qrcode yang nantinya akan dipindai oleh authy atau Google Authenticator (atau memasukkan kode rahasia yang dihasilkan). Sementara di halaman berikutnya kita buat form untuk mengecek apakah kode yang dihasilkan aplikasi authenticator cocok dengan aplikasi kita. Sederhana, bukan?

Pada aplikasi sebenarnya, proses pembuatan qrcode maupun pengecekan kode verifikasi otp mungkin akan dilakukan di server. Jadi android hanya meneruskan saja. Tapi jika bisa dibuat di Android, kenapa tidak, bukan?

Sebelumnya, mari kita bahas teorinya dulu. Kode 2FA yang dihasilkan aplikasi authenticator dihasilkan dari algoritma Time-based One Time Password (TOTP). Menurut wikipedia, TOTP merupakan ekstensi dari HMAC-based One Time Password (HOTP).

Algoritma ini menghasilkan kode OTP yang berlaku selama kurun waktu tertentu. TOTP sudah diadopsi oleh IETF dengan standar RFC 6238.

Masih dari wikipedia, kode TOTP diperoleh dari:

TOTP_value(K) = HOTP_value(K, C_T)

di mana

HOTP_Value = HOTP(K, C) mod 10^d

Untuk penjelasan variabel-variabelnya silakan baca sendiri artikelnya, takut salah 😀

Kembali ke Android. Secara ringkas, ada dua menu yang akan kita buat, yaitu menu generate qrcode dan kode rahasia, serta menu verifikasi kode otp.

Membuat QRCode OTP

Kita tidak akan membuat implementasi algoritma TOTP langsung dari mentah alias from scratch. Ada banyak librari opensource yang bisa kita manfaatkan. Saya sendiri menggunakan beberapa librari, yakni librari yang menghasilkan kode rahasia dan URI otpAuth, serta librari untuk menampilkan qrcode.

Untuk yang pertama, saya gunakan librari buatan Sam Stevens, yakni java-totp. Librari ini sebenarnya tidak khusus untuk Android, tapi secara umum bisa digunakan untuk proyek yang pakai bahasa Java.

Sebenarnya librari ini sudah cukup lengkap, karena sudah disediakan pula fungsi untuk menghasilkan qrcode dari kode rahasia yang dihasilkan. Sayangnya, fungsi tersebut tidak jalan di Android. Karena itulah kita butuh librari lain yang bisa menghasilkan gambar qrcode.

Librari yang saya pilih adalah QR-code-generator milik JamOrHam. Librari ini merupakan fork dari proyek Nauyki/QR-code-generator yang lebih dikhususkan untuk Android.

Oke, mari kita mulai ngoding. Langkah pertama dalam membuat qrcode otp adalah membuat kode rahasia dan URI otpAuth. Berikut sekilas kodenya:

val secretGenerator = DefaultSecretGenerator()
val secret = secretGenerator.generate()
val data = QrData.Builder()
  .label("my totp App label")
  .secret(secret)
  .issuer("MyTotpApp")
  .algorithm(HashingAlgorithm.SHA512)
  .digits(6)
  .period(30).build()

Kode tersebut akan menghasilkan variabel data bertipe QrData. Perhatikan bahwa nantinya otp kita akan memiliki 6 digit dengan durasi kedaluwarsanya selama 30 detik. Label dibutuhkan agar ketika qrcode dipindai, pengguna akan tahu qrcode tersebut dari aplikasi mana. Maka isilah label tersebut dengan nama aplikasi kita. Sementara secret adalah kode rahasia yang akan ditampilkan di aplikasi. Panjang secret sepanjang 32 karakter.

val dataUri = data.uri
var secretSpace = ""
secret.forEachIndexed { index, c ->
  secretSpace += if ((index != 0) && (index % 4 == 0)) "$c " else c
}

Dari kode di atas, kita dapatkan URI otpAuth dan membuat kode rahasia yang dihasilkan menjadi relatif mudah dibaca, karena setiap 4 karakter akan diberi spasi (tapi kodenya masih belum benar).

Pada aplikasi beneran, kode secret akan disimpan di database server. Tapi untuk sekarang, cuma disimpan di sharedpreferences saja.

val pref = activity?.getPreferences(Context.MODE_PRIVATE)
if (pref != null) {
  with(pref.edit()) {
    putString(getString(R.string.otp_key), secret)
    apply()
  }
}

Setelah URI otpAuth dan kode secret sudah dapat, saatnya membuat qrcode. Caranya cukup simpel.

val qr = QrCode.encodeText(dataUri, QrCode.Ecc.MEDIUM)
val qrImage = qr.toSvgString(1)

Hasilnya adalah sebuah string berisi berkas SVG.

Untuk menampilkan gambar, saya gunakan librari coil karena sudah mendukung format svg dengan relatif mudah dibanding Glide. Selain itu, coil dibuat 100% menggunakan kotlin :D.

Agar coil dapat menampilkan gambar svg, pada berkas build.gradle, import librari pendukung svg:

implementation "io.coil-kt:coil:1.1.0"
implementation "io.coil-kt:coil-svg:1.1.0"

Setelah itu, buatlah berkas imageLoader yang mendukung svg.

val imageLoader = context?.let {
  ImageLoader.Builder(it).componentRegistry {
    add(SvgDecoder(it))
  }.build()
}

Sebenarnya, qrImage masih berupa string biasa, bukan berkas svg. Karena itu, kita buat berkas temporer yang nantinya akan dimuat di imageview.

val fileTemp = File.createTempFile("qrtemp", ".svg")
val bw = BufferedWriter(FileWriter(fileTemp))
bw.write(qrImage)
bw.close()
val path = fileTemp.absolutePath

path inilah yang nantinya akan dimuat di imageView. Sekarang, kita persiapkan widget ImageView agar bisa menampilkan berkas svg. Jangan lupa juga untuk menampilkan kode rahasia di TextView.

val imgView = view.findViewById(R.id.img)
imageLoader?.let { imgView.load(File(path), it) }
view.findViewById(R.id.tv_secret).text = "$secretSpace"

Kode lengkapnya bisa dilihat di sini. Contoh hasilnya seperti gambar di bawah.

Untuk mencobanya, silakan pindai qrcode yang dihasilkan menggunakan aplikasi autentikator macam Authy atau Google Authenticator. Jika tidak ada halangan, harusnya secara otomatis, aplikasi kita akan langsung terbaca seperti gambar di bawah.

Verifikasi kode otp

Langkah verifikasi kode otp yang dihasilkan aplikasi autentikator cukup mudah. Pertama, kita ambil nilai secret dari sharedpreferences. Setelah itu, menggunakan nilai secret tersebut, kita lakukan verifikasi. Langkah verifikasi masih menggunakan librari java-totp.

private fun checkTotp(token: String): Boolean {
  val pref = activity?.getSharedPreferences(getString(R.string.preference_name), Context.MODE_PRIVATE)
  if (pref != null) {
    val secret = pref.getString(getString(R.string.otp_key),"")
    val timeProvider = SystemTimeProvider()
    val codeGenerator = DefaultCodeGenerator()
    val verifier = DefaultCodeVerifier(codeGenerator, timeProvider)
    return verifier.isValidCode(secret, token)
  }
  return false
}

Fungsi checkTotp akan membandingkan kode otp yang dimasukkan user dengan secret yang disimpan di sharedpreferences. Jika nilainya cocok, maka outputnya true alias berhasil, sebaliknya false alias gagal. Kode lengkapnya bisa dilihat di sini.

Bagaimana bund, mudah bukan?


Ada komentar?

This site uses Akismet to reduce spam. Learn how your comment data is processed.