วันอังคารที่ 17 กันยายน พ.ศ. 2556

ทำความรู้จักการเข้ารหัสแบบ Bcrypt

สมัยก่อนนิยมการเข้ารหัสแบบ md5 หรือ sha1 ซึ่งความปลอดภัยในสมัยนี้ใช้ไม่ได้แล้ว เนื่องจากมีโปรแกรมที่สามารถถอดรหัส(Decryption) ได้นั่นเอง

ปัจจุบันมี algorithm Bcrypt  ที่ใช้เข้ารหัส (Encryption) โดย Eksblowfish Algorithm ที่เข้ารหัสแบบทางเดียว(one-way hashing algorithm.) คือไม่สามารถถอดกลับมาเป็นเหมือนเดิมได้ ถึงต่อให้ได้ ผลลัพธ์ของการเข้ารหัสก็จะไม่ได้เหมือนเดิมทุกครั้ง เพราะใน algorithm มีการใช้ round เข้ามาด้วย ซึ่งต่างจาก md5 หรือ sha1 ที่จะได้ผลลัพธ์จากการเข้ารหัสเหมือนกันทุกๆ ครั้ง

ตัวอย่างคลาส Bcrypt 
class Bcrypt {
  private $rounds;
  public function __construct($rounds = 12) {
    if(CRYPT_BLOWFISH != 1) {
      throw new Exception("bcrypt not supported in this installation. See http://php.net/crypt");
    }

    $this->rounds = $rounds;
  }

  public function hash($input) {
    $hash = crypt($input, $this->getSalt());

    if(strlen($hash) > 13)
      return $hash;

    return false;
  }

  public function verify($input, $existingHash) {
    $hash = crypt($input, $existingHash);

    return $hash === $existingHash;
  }

  private function getSalt() {
    $salt = sprintf('$2a$%02d$', $this->rounds);

    $bytes = $this->getRandomBytes(16);

    $salt .= $this->encodeBytes($bytes);

    return $salt;
  }

  private $randomState;
  private function getRandomBytes($count) {
    $bytes = '';

    if(function_exists('openssl_random_pseudo_bytes') &&
        (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN')) { // OpenSSL slow on Win
      $bytes = openssl_random_pseudo_bytes($count);
    }

    if($bytes === '' && is_readable('/dev/urandom') &&
       ($hRand = @fopen('/dev/urandom', 'rb')) !== FALSE) {
      $bytes = fread($hRand, $count);
      fclose($hRand);
    }

    if(strlen($bytes) < $count) {
      $bytes = '';

      if($this->randomState === null) {
        $this->randomState = microtime();
        if(function_exists('getmypid')) {
          $this->randomState .= getmypid();
        }
      }

      for($i = 0; $i < $count; $i += 16) {
        $this->randomState = md5(microtime() . $this->randomState);

        if (PHP_VERSION >= '5') {
          $bytes .= md5($this->randomState, true);
        } else {
          $bytes .= pack('H*', md5($this->randomState));
        }
      }

      $bytes = substr($bytes, 0, $count);
    }

    return $bytes;
  }

  private function encodeBytes($input) {
    // The following is code from the PHP Password Hashing Framework
    $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

    $output = '';
    $i = 0;
    do {
      $c1 = ord($input[$i++]);
      $output .= $itoa64[$c1 >> 2];
      $c1 = ($c1 & 0x03) << 4;
      if ($i >= 16) {
        $output .= $itoa64[$c1];
        break;
      }

      $c2 = ord($input[$i++]);
      $c1 |= $c2 >> 4;
      $output .= $itoa64[$c1];
      $c1 = ($c2 & 0x0f) << 2;

      $c2 = ord($input[$i++]);
      $c1 |= $c2 >> 6;
      $output .= $itoa64[$c1];
      $output .= $itoa64[$c2 & 0x3f];
    } while (1);

    return $output;
  }
}
วิธีเรียกใช้งาน
$bcrypt = new Bcrypt(15);

$hash = $bcrypt->hash('password'); 
$check= $bcrypt->verify('password', $hash);
รายละเอียดดูเพิ่มเติมได้ ที่นี่

4 ความคิดเห็น:

  1. มีวิธีการที่จะถอดรหัส Bcrypt ได้ไหมครับ ถ้าไม่มีเพราะเหตุผลอะไรครับ

    ตอบลบ
  2. ไม่ระบุชื่อ3 เมษายน 2563 เวลา 13:40

    ไม่มีครับ เพราะ bcrypt เป็นการเข้ารหัสทางเดียว (one-way encryption) ซึ่งไม่สามารถถอดกลับไปเป็นค่าเดิมได้ แต่สามารถใช้ tool เทียบค่าของ string กับ bcrypt hash ได้ เพื่อเช็กว่า bcrypt hash นี้มาจาก string นี้หรือเปล่า ซึ่งวีธีการนี้คือการ verify password ของ user ตอน login นั่นเองครับ

    https://www.appdevtools.com/bcrypt-generator

    ตอบลบ
  3. MD5, SHA1, SHA2, SHA3 ทั้งหมดเป็น one way encryption ครับ ไม่สามารถ decrypt ได้ครับ แม้กระทั่งปัจจุบัน ที่บางเว็บทำหลอกว่า decrypt ได้ จริงๆแล้วไม่ใช่ครับ เขาใช่วิธีการทำ index ข้อของ text และค่า Hash ไว้แล้วใช้วิธีไปค้นหาจากค่าที่ทำ index ไว้ครับ

    ตอบลบ
  4. ฺBcrypt at least 13 เลข 13 คืออะไรครับ

    ตอบลบ