Minggu, 21 November 2010

Membuat Web dengan Otentikasi berbasis Token


, in Web Security, by Rizki Wicaksono
Pada artikel sebelumnya saya sudah menjelaskan cukup detil tentang cara kerja token yang dipakai pada internet banking. Artikel tersebut hanya menjelaskan sebatas teoretis saja, saya pikir akan lebih baik bila ada prakteknya. Oleh karena itu, seperti yang sudah saya janjikan, kali ini saya akan membuat sebuah contoh sederhana website dengan otentikasi yang menggunakan token seperti pada situs internet banking. Aplikasi token yang saya buat ini dikembangkan dari aplikasi yang bernama Mobile-OTP, dari aplikasi itu saya tambahkan beberapa fitur agar mirip dengan token yang dipakai di internet banking. Sedangkan aplikasi server/website saya harus membuat sendiri dari awal karena tidak tersedia di Internet.
Persiapan
Ada dua komponen yang terlibat dalam sistem ini, yaitu token yang dipakai oleh client dan tentu saja server. Token ada yang berwujud fisik seperti kalkulator kecil, ada juga yang berwujud software yang disebut juga virtual token. Sebenarnya token fisik maupun software sama saja, jadi dalam artikel ini saya akan membuat token yang berwujud software. Lebih spesifik lagi token ini dibuat dengan Java Mobile sehingga bisa diinstall di handphone yang mendukung Java. Jadi wujud fisik token yang berbentuk kalkulator bisa digantikan dengan wujud fisik sebuah handphone. Dalam artikel ini saya akan menggunakan emulator handphone yang dibundel dari Java Wireless Toolkit.
Komponen lainnya adalah server. Diperlukan web server dan database server, dalam artikel ini saya pakai XAMPP yang sudah dibundel dengan Apache, PHP dan MySQL.
Spesifikasi One Time Password di Aplikasi Contoh
Dalam aplikasi contoh ini, one time password didapatkan dengan mengambil 6 karakter pertama hasil penghitungan hash dengan fungsi MD5. Granularity sistem ini adalah 10 detik, dengan kata lain setiap 10 detik token akan menghasilkan OTP yang berbeda. Jika anda meminta token mengeluarkan OTP beberapa kali dalam rentang 10 detik, maka semuanya akan menghasilkan OTP yang sama. Kemudian baru ketika waktu masuk ke 10 detik berikutnya token akan menghasilkan OTP yang baru.
Dalam aplikasi contoh ini, umur OTP adalah 3 menit, artinya server harus menghitung semua OTP dalam time window 6 menit, yaitu 3 menit ke belakang dan 3 menit ke depan relatif terhadap waktu ketika server melakukan otentikasi. Konsekuensinya adalah setiap OTP yang dihasilkan token akan dianggap valid bila belum pernah dipakai dalam 3 menit sejak OTP dibangkitkan.
Spesifikasi Software Token
Token yang akan saya buat ini nantinya memiliki fitur yang sama dengan token internet banking pada umumnya. Pada kondisi normal nilai init-secret sudah ditanam di dalam token secara hardware, namun dalam token contoh ini tersedia fitur untuk melakukan inisialisasi nilai init-secret. Init-secret ini harus dicatat juga di server agar server bisa menghasilkan OTP yang sama dengan token.
Token bisa menghasilkan OTP dalam mode Challenge/Response maupun dalam mode Response Only (self generated). Challenge yang diterima oleh token adalah sepanjang 4 digit dan menghasilkan response sepanjang 6 digit hexadesimal.
Spesifikasi Website
Website dalam aplikasi contoh ini saya buat dengan PHP. Aplikasi ini terdiri dari 2 file saja, yaitu login.php dan transfer.php. Login.php digunakan untuk melakukan login dengan menggunakan response only pin dari token. Sedangkan transfer.php adalah untuk melakukan transfer uang dengan menggunakan challenge/response pin dari token.
Aplikasi ini saya pasang pada localhost yang terinstall Apache+MySQL+PHP dari XAMPP. Sebelum bisa melakukan transfer uang, user harus melakukan login di URL https://localhost/mytestbank/login.php . Perhatikan URL tersebut sengaja saya memakai https agar mirip dengan internet banking. Tentu saja browser anda akan berontak ketika menggunakan https karena memang saya tidak punya sertifikat yang ditandatangani CA untuk localhost. Bila anda kesulitan mengonfigurasi browser anda untuk memakai https tersebut, anda boleh memakai http biasa.
Pada saat login user diminta memasukkan username dan OTP yang dihasilkan dari token dalam mode response only (self generated). Bila login berhasil, maka user dialihkan ke halaman https://localhost/mytestbank/transfer.php . Berbeda dengan halaman login, pada saat transfer user diminta untuk memasukkan OTP dari token dalam mode challenge/response. Server memberikan challenge sepanjang 4 digit angka.
Aplikasi ini juga membutuhkan sebuah tabel MySQL bernama users yang saya masukkan dalam database bernama mytestbank. Tabel ini digunakan untuk menyimpan informasi pengguna website. Berikut adalah script SQL untuk membuat tabel users.
CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `initsecret` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
Agar lebih mudah sebaiknya anda menginstall XAMPP atau WAMP dan menggunakan PhpMyAdmin untuk membuat tabel users tersebut.
Pembuatan Token
Token dibuat dengan Java Mobile (disebut midlet) yang sangat sederhana. Midlet ini hanya terdiri dari 2 file, yaitu MD5.java dan MobileOTP.java. Class MD5 adalah library untuk menghitung nilai hash MD5. Sedangkan class MobileOTP adalah class utama. Karena sourcenya cukup panjang, anda bisa mendownload sourcenya dalam file zip di sini. File zip itu bisa langsung anda extract ke dalam folder apps dalam Java Wireless Toolkit bila ingin menjalankan midlet itu dengan menggunakan emulator.
Saya akan menjelaskan fungsi utama dari token yaitu membangkitkan OTP dalam mode Challenge/Response dan mode Response Only. Perhatikan potongan kode token berikut yang berfungsi untuk membangkitkan OTP dalam mode Response Only.
now=new Date();          
epoch=""+(now.getTime()+((timez-12)*3600000));
epoch=epoch.substring(0,epoch.length()-4);
String otp=epoch+secret;
MD5 hash=new MD5(otp);
otp=hash.asHex().substring(0,6);
Kode di atas sangat sederhana, diawali dengan mengambil detik EPOCH sepanjang 9 digit (ini sama dengan EPOCH/10 karena granularitynya adalah 10 detik). Kemudian nilai EPOCH tersebut digabung dengan init-secret. String gabungan epoch dengan init secret ini dihitung hashnya dengan MD5. Kemudian 6 karakter pertama dari hash tersebut diambil sebagai OTP response only (self generated).
Kode berikut ini adalah untuk membangkitkan OTP dalam mode challenge/response. Kode tersebut sangat mirip dengan potongan kode di atas hanya perbedaannya adalah adanya challenge yang dalam kode tersebut ada pada variabel PIN. Gabungan dari epoch, init secret dan challenge dihitung nilai hashnya dengan MD5, baru kemudian diambil 6 karakter pertamanya sebagai OTP.
1
2
3
4
5
6
now=new Date();            
epoch=""+(now.getTime()+((timez-12)*3600000));
epoch=epoch.substring(0,epoch.length()-4);            
otp=epoch+secret+PIN;            
hash=new MD5(otp);            
otp=hash.asHex().substring(0,6);
Bila anda ingin mencobanya di hape anda, pastikan HP anda mendukung java. Anda bisa mengkopi file JAR aplikasi ini ke memory card anda dengan bluetooth/USB, atau anda bisa juga mendownload langsung dari browser hp anda. URL untuk mendownload file JAR midlet ini adalah http://www.ilmuhacking.com/wp-content/uploads/2009/07/MobileOTP.jar . Selanjutnya anda tinggal mengikuti petunjuk untuk instalasi aplikasi seperti biasa. Ingat sebelum dipakai diperlukan langkah inisialisasi nilai init-secret dengan cara mengetikkan #**# dalam field “CHAL”.
Berikut ini adalah screen capture dari aplikasi tersebut yang diinstall di HP saya. Dua gambar di bawah ini adalah prosedur inisialisasi init-secret ketika aplikasi pertama kali dijalankan. Nilai init secret ini harus dicatat dan disimpan ke dalam tabel user di server.
e90mobileotp1
e90mobileotp2
Gambar di bawah ini adalah screen shot ketika midlet token membangkitkan OTP dalam mode response only.
e90mobileotp5
Gambar di bawah ini adalah screen shot ketika midlet token membangkitkan OTP dalam mode challenge response.
e90mobileotp4
Pembuatan Aplikasi Web
Aplikasi web terdiri dari dua file yaitu login yang menggunakan OTP dalam mode response only, dan transfer yang menggunakan OTP dalam mode challenge response. Seperti halnya midlet token, aplikasi ini juga sangat sederhana. Fungsi utamanya adalah pada fungsi checkCR() dan checkRO() yang berfungsi untuk melakukan otentikasi dalam mode Challenge/Response atau Response Only. Berikut adalah isi dari fungsi checkCR dan checkRO.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Check Challenge/Response Mode
function checkCR($chal,$otp,$initsecret)
{
 $maxperiod = 3*60; // in seconds = +/- 3 minutes
 $time=gmdate("U");
 for($i = $time - $maxperiod; $i <= $time + $maxperiod; $i++)
 {
    $md5 = substr(md5(substr($i,0,-1).$initsecret.$chal),0,6);
    if($otp == $md5) return(true);
 }
return(false);
}
 
// Check Response/Only Mode
function checkRO($otp,$initsecret)
{
 $maxperiod = 3*60; // in seconds = +/- 3 minutes
 $time=gmdate("U");
 for($i = $time - $maxperiod; $i <= $time + $maxperiod; $i++)
 {
    $md5 = substr(md5(substr($i,0,-1).$initsecret),0,6);
    if($otp == $md5) return(true);
 }
return(false);
}
Perbedaan kedua fungsi itu hanya pada adanya $chal pada fungsi checkCR() sedangkan pada fungsi checkRO() yang digabungkan hanya epoch dan initsecret. Mari kita ulas kedua fungsi tersebut karena inti dari aplikasi ini ada pada kedua fungsi itu.
Dalam artikel sebelumnya saya sudah menjelaskan mengenai time window atau toleransi yang diberikan server ketika melakukan otentikasi. Dalam contoh ini time window yang diberikan server adalah 3 menit ke depan dan 3 menit ke belakang relatif terhadap waktu ketika server melakukan otentikasi. Waktu (dalam detik EPOCH) ketika server melakukan otentikasi disimpan pada variabel $time (baris ke-5 dan baris ke-18), sehingga server harus menghitung semua otp dari $time-180 hingga $time+180 (3 menit ke belakang dan 3 menit ke depan) pada baris ke-6 dan baris-19.
Selanjutnya pada barus ke-8 dan baris ke-21 server menghitung otp dengan mengambil 6 karakter awal dari fungsi hash gabungan dari epoch+initsecret dan challenge (khusus untuk checkCR).
Di bawah ini adalah source untuk file login.php. Untuk melakukan otentikasi, login.php memanggil fungsi checkRO() pada baris ke-12. Namun untuk memanggil fungsi checkRO() dibutuhkan init secret dari user yang disimpan dalam tabel users sehingga server harus melakukan query ke tabel users (baris ke-8). Selanjutnya bila otentikasi berhasil server akan menyimpan username dan initsecret pada session kemudian mengalihkan user ke aplikasi transfer (transfer.php).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

session_start();
include("initdb.php");
include("lib.php");
if (isset($_POST["login"])) {
 $username = addslashes($_POST["username"]);
 $pin = $_POST["pin"];
 $sql = "select initsecret from users where username='$username' ";
 $res=mysql_query($sql);
 if ($arr_row=mysql_fetch_array($res)) {
  $initsecret = $arr_row[0];
  $success = checkRO($pin,$initsecret);
  if ($success) {
   $_SESSION["username"] = $username;
   $_SESSION["initsecret"] = $initsecret;   
   header("Location: transfer.php");
  } else {
   $err = "Wrong PIN, Try Again.";
  }
 } else {
  $err = "Wrong PIN, Try Again.";
 }
}
?>


Login Internet Banking MyTestBank

if (isset($err)) echo "$err"; ?>
Username
Token PIN:
Di bawah ini adalah source untuk file transfer.php. Untuk melakukan otentikasi server memanggil fungsi checkCR() pada baris ke-13. Fungsi checkCR ini membutuhkan init secret, challenge yang diambil dari session. Kemudian server akan memberikan pesan “Transfer Success” atau “Wrong PIN” tergantung dari hasil fungsi checkCR().
Pada baris ke-21 dan ke-22 server membangkitkan nilai acak sepanjang 4 digit sebagai challenge. Challenge ini disimpan dalam session agar tidak bisa dimanipulasi user. Bila challenge disimpan sebagai hidden field dalam form, maka user bisa bebas mengubah isi challenge itu.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

session_start();
include("initdb.php");
include("lib.php");
if (!isset($_SESSION["initsecret"])) {
 header("Location: login.php");
}
if (isset($_POST["login"])) {
 $username = addslashes($_POST["username"]);
 $pin = addslashes($_POST["pin"]);
 $initsecret = $_SESSION["initsecret"];
 $challenge = $_SESSION["challenge"];
 $success = checkCR($challenge,$pin,$initsecret);
 
 if ($success) {
  $msg = "Transfer Success";
 } else {
  $msg = "Wrong PIN";
 }
} 
srand(time());
$challenge = sprintf("%04d",(rand()%9999));
$_SESSION["challenge"] = $challenge;
 
?>


 if (isset($msg)) echo "$msg"; ?>

Transfer Form

No Rekening Tujuan: Jumlah: Rp. Challenge Code: $challenge ?>(Masukkan kode ini ke dalam token anda) Token PIN : (Masukkan response dari token anda)
Anda bisa mendownload semua source php di sini.
Test
Sebelum mencoba pertama saya harus menyamakan jam di token (handphone) dengan jam di server. Setelah sama baru kita uji coba aplikasi ini dengan skenario berikut:
Nasabah myTestBank ingin memakai aplikasi internet banking myTestBank untuk mentransfer sejumlah uang. Untuk itu dia baru saja mendownload Midlet Token dan menginstallnya ke HPnya. Token midlet tersebut diinisialisasi dengan nilai init secret 369e4a62be0e579a. Setelah mendaftar user tersebut mendapat username rizki. Untuk menggunakan fasilitas ini dia harus membuka browser ke URL https://localhost/mytestbank/login.php .
Selain menyamakan jam di token dan di server, init secret di token dan di server harus sama. Gambar di bawah ini menunjukkan bahwa init secret di hape saya sudah sama dengan yang di tabel users MySQL.
initsecret-server-token
Kini user sudah siap mencoba untuk login ke aplikasi MyTestBank di URL https://localhost/mytestbank/login.php . Berikut ini adalah screen capture ketika user login.
loginotp
Setelah login berhasil, kemudian user dihadapkan pada form untuk melakukan transfer uang. Berikut ini adalah screen shot pada browser dan hp user ketika user melakukan transfer.
transferformOTP
Setelah memasukkan OTP dengan benar, server memberikan informasi “Transfer Success”. Kini user telah berhasil melakukan transfer. Berikut adalah screenshot ketika transfer berhasil dilakukan.
transfersuccess
Share and Enjoy:
  • Slashdot
  • StumbleUpon
  • del.icio.us
  • Digg
  • Facebook
  • MySpace
  • Technorati
  • Google Bookmarks
  • Reddit
  • Live
  • YahooMyWeb
  • Mixx

0 komentar:

Posting Komentar