Categories
Dokumentasi

General Purpose Hashing vs Password Hashing

Salah satu kunci keamanan data adalah kerahasiaan dan penyandian (kriptografi). Penyandian sendiri ada yang bisa diubah ke pesan asli dan ada yang tidak dapat diubah ke pesan asli.

Apakah perbedaan General Purpose Hashing, Password Hashing dan Enkripsi? Apa pula itu Plain Text, Salt, Pepper, Entropy? Apa tips berkaitan dengan password?

— General Purpose Hashing vs Password Hashing
https://pdsi.unisayogya.ac.id/general-purpose-hashing-vs-password-hashing/ 2021-03-04 13:21:34

Daftar Isi

Istilah

|-Plain Text

Plain text kurang lebih adalah pesan atau teks asli, baik dapat dimengerti maupun tidak.

Contoh: Hallo, $%abc, dan sebagainya.

|-Enkripsi vs Hash

Enkripsi kurang lebih adalah proses menyandikan plain text agar aman ketika dikirim dan dapat diubah ke dalam plain text ketika sampai di tujuan pengiriman melalui proses yang dinamakan dengan dekripsi. Proses enkripsi dan dekripsi membutuhkan teks sebagai kunci.

Contoh:
Plain text → Hallo
Kunci → 123
Enkripsi → enkrip(Hallo, 123) → G5HT7opFNsg=
Dekripsi→ enkrip(G5HT7opFNsg=, 123) → Hallo

Hash kurang lebih adalah proses penyandian plain text satu arah sedemikian sehingga sandi tidak dapat dikembalikan lagi menjadi plain text.

|-Entropi

Entropi kurang lebih adalah seberapa banyak variasi di dalam password.

Ketika password menggunakan huruf kecil, maka hanya ada 26 karakter untuk variasi. Alfa numerik lebih baik karena terdapat 36 karakter. Namun, akan lebih baik lagi apabila mengkombinasikan huruf kapital, huruf kecil, angka dan simbol yang akan menjadi 96 karakter.

|-Salt, Pepper

Salt kurang lebih adalah tambahan teks dalam penyandian, biasanya digabungkan dengan plain text sebelum disandikan. Apabila terdapat pepper, maka salt berada di tempat yang sama dengan password yang sudah disandikan berada.

Pepper kurang lebih adalah sama dengan salt, tetapi diletakkan di tempat yang berbeda dengan password yang sudah disandikan berada. Pepper berguna untuk mempersulit pembobol membongkar password karena ada teks tambahan yang tidak ada di dalam database bersama dengan password. Baca: https://security.stackexchange.com/a/3289

Contoh:
Plain text → Hallo
Salt → 123 → diletakkan di tabel di database
Pepper → 456 → misalnya diletakkan di config.php
Sandi → hash(Hallo123456) → disimpan di tabel di database

|-General Purpose Hashing

General Purpose Hashing kurang lebih adalah algoritma hash yang didesain untuk dapat memberikan hash pada berkas yang besar dan prosesnya terjadi dengan cepat. Biasanya digunakan untuk checksum atau memeriksa integritas dari teks atau berkas. Algoritma general purpose hashing antara lain md5, sha256, dan sebagainya.

|-Password Hashing

Password Hashing kurang lebih adalah algoritma hash yang didesain khusus untuk password dan tidak cepat, sehingga membuat pembobol membutuhkan waktu dan dana yang sangat banyak untuk mencoba memecahkan plain text dari password tersebut. Algoritma general purpose hashing antara lain argon2, scrypt, bcrypt, dan sebagainya.

Tips Password

  1. Gunakan password hashing + salt + pepper untuk menyimpan password. Hanya gunakan general purpose hashing untuk melakukan hash terhadap salt atau pepper.
  2. General purposes hashing juga dapat digunakan sebagai plain text untuk password hashing. Apabila mengkombinasikan beberapa hash, maka output dari hash sebelumnya perlu di-base64_encode atau di-hex, termasuk untuk salt atau pepper. Baca: https://blog.ircmaxell.com/2015/03/security-issue-combining-bcrypt-with.html
  3. Sebaiknya password disimpan di dalam kolom berupa varchar(255). Baca: https://www.php.net/manual/en/function.password-hash.php
  4. Jangan pernah mencatat (log) password, baik sengaja maupun tidak. Tidak sengaja contohnya:
    1. mengirim password dengan metode GET yang bisa jadi akan masuk ke dalam history dari browser
    2. query dengan plain text yang bisa masuk ke slow log database atau log lainnya
  5. Implementasikan minimal 8-10 karakter untuk password dengan huruf kapital, huruf kecil, angka dan simbol. Jangan batasi maksimal karakter yang akan digunakan.

Contoh Implementasi Password

|-Persiapan

  1. Buat tabel yang minimal terdapat salt dan algoritma yang digunakan untuk melakukan hash
  2. Buat fungsi untuk membuat random salt. Gunakan fungsi tersebut untuk membuat salt. Pepper dikirim dari aplikasi, misalnya web.
  3. Buat fungsi untuk membuat hash dari password
  4. Buat fungsi untuk cek password. Gunakan fungsi tersebut untuk memvalidasi password dan apabila terdapat algoritma baru untuk hash password, maka salt dan password di update

Buat tabel contoh:

USE `test`;
CREATE TABLE `user` (
  `iduser` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `password` varchar(255) DEFAULT NULL,
  `salt` varchar(255) DEFAULT NULL,
  `algorithm` tinyint(3) unsigned DEFAULT '1',
  PRIMARY KEY (`iduser`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Buat skrip untuk menangani password:

<?php
class pass_sec
{
 private $salt_;
 private $pepper_;
 private $latestalgorithm_;
 private $conn_;
 
 function pass_sec()
 {
  //----- must be change once -----//
  $this->pepper_          = 'MGVhYTI3MDEwZW=';
  //----- +1 if new algorithm added -----//
  $this->latestalgorithm_ = 1;
 }
 
 function hash_password($plaintext_, $salt_, $algorithm_)
 {
  switch ($algorithm_)
  {
   //----- write new algorithm here -----//
   case 2:
   default:
   //----- change with your current algorithm -----//
   //----- maybe return plaintext_; -----//
   //----- or return hash('md5', $plaintext_); -----//
    return hash('sha512', $plaintext_ . $salt_ . $this->pepper_);
  }
 }
 
 function verify_password($plaintext_, $salt_, $algorithm_, $password_)
 {
  switch ($algorithm_)
  {
   //----- write how to verify new algorithm here -----//
   case 2:
   default:
   //----- change with your current verification algorithm -----//
    return $this->hash_password($plaintext_, $salt_, $algorithm_) == $password_;
  }
 }

 function safe_random_salt()
 {
  return substr(base64_encode( hash('sha512', uniqid()) ),0,255);
 }
 
 function password_check($iduser_, $plaintext_)
 {
  $row = $this->db_get_password($iduser_);
  
  if ( $this->verify_password($plaintext_, $row['salt'], $row['algorithm'],$row['password']) )
  {
   if ( $row['algorithm'] < $this->latestalgorithm_ )
   {
    $salt_ = $this->safe_random_salt();
    $this->db_update_password($iduser_, $plaintext_, $salt_);
   }
   return true;
  }
  else
  {
   return false;
  }
 }
 
 function bcrypt_test()
 {
  //https://www.php.net/manual/en/function.password-hash.php
  $timeTarget = 0.05; // 50 milliseconds 
  $cost = 8;
  do 
  {
   $cost++;
   $start = microtime(true);
   password_hash("test", PASSWORD_BCRYPT, ["cost" => $cost]);
   $end = microtime(true);
  } 
  while (($end - $start) < $timeTarget);

  echo "Appropriate Cost Found: " . $cost;
 }
 
 function db_connect($servername, $username, $password, $dbname)
 {
  $this->conn_ = new mysqli($servername, $username, $password, $dbname);
  $this->conn_->options(MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, true);
  $this->conn_->ssl_set('/etc/mysql/ssl/client-key.pem', '/etc/mysql/ssl/client-cert.pem', '/etc/mysql/ssl/ca-cert.pem', NULL, NULL);
  $this->conn_->real_connect($servername, $username, $password, $dbname, 3306, NULL, MYSQLI_CLIENT_SSL);
 }
 
 function db_get_password($iduser_)
 {
  if (!$this->conn_->connect_error)
  {
   $stmt   = $this->conn_->prepare("select salt, password, algorithm from user where iduser=? limit 0,1");
   $stmt->bind_param("i", $iduser_);
   $stmt->execute();
   $result = $stmt->get_result();
   while ($row = $result->fetch_assoc())
   {
    if (method_exists($stmt,'close'))
    {
     $stmt->close();
    }
    return $row;
   }
  }
 }
 
 function db_update_password($iduser_, $plaintext_, $salt_)
 {
  if (!$this->conn_->connect_error)
  {
   $stmt = $this->conn_->prepare("update user set salt=?, algorithm=?, password=? where iduser=?");
   $newpass = $this->hash_password($plaintext_, $salt_, $this->latestalgorithm_);
   $stmt->bind_param("sisi", $salt_, $this->latestalgorithm_, $newpass, $iduser_);
   $stmt->execute();
   if (method_exists($stmt,'close'))
   {
    $stmt->close();
   }   
  }
 }
 
 function db_simulate_insert($ids_)
 {
  $stmt  = $this->conn_->prepare("insert into user (password, salt, algorithm) values (?, ?, ?)");
  foreach ($ids_ as $id)
  {
   $salt_ = $this->safe_random_salt();
   $stmt  = $this->conn_->prepare("insert into user (iduser, password, salt, algorithm) values (?, ?, ?, ?) on duplicate key update iduser=iduser");
   $pass  = $this->hash_password('this is a password', $salt_, $this->latestalgorithm_);
   $stmt->bind_param("issi", $id, $pass, $salt_, $this->latestalgorithm_);
   $stmt->execute();
  }
  if (method_exists($stmt,'close'))
  {
   $stmt->close();
  }
 }
}

|-Testing Persiapan

Contoh input:

<?php
$pass_test = new pass_sec();
$pass_test->db_connect('localhost', 'yourdbuser', 'yourdbpass', 'dbname');
$pass_test->db_simulate_insert([1,2]);
echo '<br/>Test ID 1 with right password '.$pass_test->password_check(1, 'this is a password');
echo '<br/>Test ID 2 with right password '.$pass_test->password_check(2, 'this is a password');
echo '<br/>Test ID 1 with wrong password '.$pass_test->password_check(1, 'this is a passwords');
echo '<br/>Test ID 2 with wrong password '.$pass_test->password_check(2, 'this is a passwords');

Contoh di tabel:

password yang tersimpan di basis data
password yang tersimpan di basis data

|-Perubahan Hash

Apabila ada perubahan hash, maka cukup lakukan modifikasi di 3 tempat, yaitu:

  1. //—– +1 if new algorithm added —–//
    inkremen $this->latestalgorithm_, misalnya menjadi $this->latestalgorithm_ = 2;
  2. //—– write new algorithm here —–//
    ditambahkan algoritma, misalnya menjadi
//----- write new algorithm here -----//
case 2:
    $options = ['cost' => 12];
    $hashedplaintext = base64_encode(hash('sha256', $plaintext_ . $salt_ . $this->pepper_));
    return password_hash($hashedplaintext, PASSWORD_BCRYPT, $options);
  1. //—– write how to verify new algorithm here —–//
    ditambahkan algoritma, misalnya menjadi
//----- write how to verify new algorithm here -----//
   case 2:
    $hashedplaintext = base64_encode(hash('sha256', $plaintext_ . $salt_ . $this->pepper_));
    return password_verify($hashedplaintext, $password_);

Sehingga skripnya menjadi seperti di bawah ini:

 function pass_sec()
 {
  //----- must be change once -----//
  $this->pepper_          = 'MGVhYTI3MDEwZW=';
  //----- +1 if new algorithm added -----//
  $this->latestalgorithm_ = 2;
 }
 
 function hash_password($plaintext_, $salt_, $algorithm_)
 {
  switch ($algorithm_)
  {
   //----- write new algorithm here -----//
   case 2:
    $options = ['cost' => 12];
    return $hashedplaintext = base64_encode(hash('sha256', $plaintext_ . $salt_ . $this->pepper_));
    password_hash($hashedplaintext, PASSWORD_BCRYPT, $options);
   default:
   //----- change with your current algorithm -----//
   //----- maybe return plaintext_; -----//
   //----- or return hash('md5', $plaintext_); -----//
    return hash('sha512', $plaintext_ . $salt_ . $this->pepper_);
  }
 }
 
 function verify_password($plaintext_, $salt_, $algorithm_, $password_)
 {
  switch ($algorithm_)
  {
   //----- write how to verify new algorithm here -----//
   case 2:
    $hashedplaintext = base64_encode(hash('sha256', $plaintext_ . $salt_ . $this->pepper_));
    return password_verify($hashedplaintext, $password_);
   default:
   //----- change with your current verification algorithm -----//
    return $this->hash_password($plaintext_, $salt_, $algorithm_) == $password_;
  }
 }

|-Testing Upgrade Algoritma Password

Contoh input:

<?php
$pass_test = new pass_sec();
$pass_test->db_connect('localhost', 'yourdbuser', 'yourdbpass', 'dbname');
$pass_test->db_simulate_insert([3,4]);
echo '<br/>Test ID 1 with right password '.$pass_test->password_check(1, 'this is a password');
echo '<br/>Test ID 2 with right password '.$pass_test->password_check(2, 'this is a password');
echo '<br/>Test ID 1 with wrong password '.$pass_test->password_check(1, 'this is a passwords');
echo '<br/>Test ID 2 with wrong password '.$pass_test->password_check(2, 'this is a passwords');
echo '<br/>Test ID 3 with right password '.$pass_test->password_check(3, 'this is a password');
echo '<br/>Test ID 4 with right password '.$pass_test->password_check(4, 'this is a password');
echo '<br/>Test ID 3 with wrong password '.$pass_test->password_check(3, 'this is a passwords');
echo '<br/>Test ID 4 with wrong password '.$pass_test->password_check(4, 'this is a passwords');

Contoh di tabel:

password yang tersimpan di basis data algoritma 2
password yang tersimpan di basis data algoritma 2

By basit

Biro Pengembangan Teknologi Dan Sistem Informasi

One reply on “General Purpose Hashing vs Password Hashing”

Leave a Reply

Your email address will not be published. Required fields are marked *

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