Bagaimana Anda menggunakan bcrypt untuk hashing password di PHP?

1256

Sesekali saya mendengar saran "Gunakan bcrypt untuk menyimpan kata sandi dalam PHP, aturan bcrypt".

Tapi apa itu bcrypt? PHP tidak menawarkan fungsi seperti itu, Wikipedia mengoceh tentang utilitas file-enkripsi dan pencarian Web hanya mengungkapkan beberapa implementasi Blowfish dalam berbagai bahasa. Sekarang Blowfish juga tersedia di PHP via mcrypt, tetapi bagaimana itu membantu menyimpan kata sandi? Blowfish adalah sandi tujuan umum, ia bekerja dua cara. Jika itu bisa dienkripsi, itu bisa didekripsi. Kata sandi memerlukan fungsi hashing satu arah.

Apa penjelasannya?

Vilx-
sumber
13
Pertanyaan ini telah diatasi sebelumnya , dan saran mereka untuk menggunakan perpustakaan standar sangat bagus. Keamanan adalah masalah yang rumit, dan dengan menggunakan paket yang dirancang oleh seseorang yang tahu apa yang mereka lakukan, Anda hanya membantu diri sendiri.
eykanal
59
@eykanal - halaman itu bahkan tidak menyebutkan bcrypt, apalagi menjelaskan apa itu .
Vilx-
8
@eykanal - Saya tidak meminta penjelasan tentang cara kerjanya. Saya hanya ingin tahu apa itu. Karena apa pun yang saya dapat menggali di internet di bawah kata kunci "bcrypt", tidak dapat digunakan untuk hashing kata sandi. Tidak secara langsung, dan tidak dalam PHP. OK, sekarang saya mengerti bahwa itu benar-benar paket "phpass" yang menggunakan blowfish untuk mengenkripsi kata sandi Anda dengan kunci yang berasal dari kata sandi Anda (pada dasarnya mengenkripsi kata sandi itu sendiri). Tetapi merujuknya sebagai "bcrypt" sangat menyesatkan, dan itulah yang ingin saya perjelas dalam pertanyaan ini.
Vilx-
3
@Vilx: Saya telah menambahkan lebih banyak informasi mengapa bcryptalgoritma hashing satu arah versus skema enkripsi dalam jawaban saya . Ada seluruh kesalahpahaman yang bcrypthanya Blowfish padahal sebenarnya memiliki jadwal kunci yang sama sekali berbeda yang memastikan bahwa teks biasa tidak dapat dipulihkan dari teks sandi tanpa mengetahui keadaan awal sandi (garam, putaran, kunci).
Andrew Moore
1
Juga lihat kerangka hashing kata sandi Portable PHP (PHPass) dari Openwall. Itu mengeras terhadap sejumlah serangan umum pada kata sandi pengguna.
jww

Jawaban:

1065

bcryptadalah algoritma hashing yang dapat diskalakan dengan perangkat keras (melalui sejumlah putaran yang dapat dikonfigurasi). Kelambanan dan ronde yang berulang memastikan bahwa penyerang harus menggunakan dana besar dan perangkat keras untuk dapat memecahkan kata sandi Anda. Tambahkan ke garam per kata sandi ( bcryptMEMBUTUHKAN garam) dan Anda dapat yakin bahwa serangan hampir tidak mungkin terjadi tanpa jumlah dana atau perangkat keras yang menggelikan.

bcryptmenggunakan algoritma Eksblowfish untuk kata sandi hash. Meskipun fase enkripsi Eksblowfish dan Blowfish persis sama, fase jadwal kunci dari Eksblowfish memastikan bahwa keadaan selanjutnya bergantung pada garam dan kunci (kata sandi pengguna), dan tidak ada negara yang dapat dikomputasi tanpa sepengetahuan keduanya. Karena perbedaan kunci ini, bcryptadalah algoritma hashing satu arah. Anda tidak dapat mengambil kata sandi teks biasa tanpa mengetahui garam, putaran dan kunci (kata sandi). [ Sumber ]

Cara menggunakan bcrypt:

Menggunakan PHP> = 5.5-DEV

Fungsi hashing kata sandi kini telah dibangun langsung ke dalam PHP> = 5.5 . Anda sekarang dapat menggunakan password_hash()untuk membuat bcrypthash kata sandi apa pun:

<?php
// Usage 1:
echo password_hash('rasmuslerdorf', PASSWORD_DEFAULT)."\n";
// $2y$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// For example:
// $2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a

// Usage 2:
$options = [
  'cost' => 11
];
echo password_hash('rasmuslerdorf', PASSWORD_BCRYPT, $options)."\n";
// $2y$11$6DP.V0nO7YI3iSki4qog6OQI5eiO6Jnjsqg7vdnb.JgGIsxniOn4C

Untuk memverifikasi kata sandi yang diberikan pengguna terhadap hash yang ada, Anda dapat menggunakan password_verify()seperti itu:

<?php
// See the password_hash() example to see where this came from.
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';

if (password_verify('rasmuslerdorf', $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}

Menggunakan PHP> = 5.3.7, <5.5-DEV (juga RedHat PHP> = 5.3.3)

Ada pustaka kompatibilitas pada GitHub yang dibuat berdasarkan kode sumber dari fungsi-fungsi di atas yang aslinya ditulis dalam C, yang menyediakan fungsionalitas yang sama. Setelah pustaka kompatibilitas diinstal, penggunaannya sama dengan di atas (minus notasi susunan steno jika Anda masih di cabang 5.3.x).

Menggunakan PHP <5.3.7 ( DEPRECATED )

Anda dapat menggunakan crypt()fungsi untuk menghasilkan bcrypt hashes dari string input. Kelas ini dapat secara otomatis menghasilkan garam dan memverifikasi hash yang ada terhadap input. Jika Anda menggunakan versi PHP yang lebih tinggi atau sama dengan 5.3.7, sangat disarankan Anda menggunakan fungsi bawaan atau pustaka compat . Alternatif ini disediakan hanya untuk tujuan historis.

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 is slow on Windows
      $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 (true);

    return $output;
  }
}

Anda dapat menggunakan kode ini seperti ini:

$bcrypt = new Bcrypt(15);

$hash = $bcrypt->hash('password');
$isGood = $bcrypt->verify('password', $hash);

Atau, Anda juga dapat menggunakan Kerangka Hashing PHP Portabel .

Andrew Moore
sumber
7
@ Kutu Jahat: Maaf mengecewakan Anda, tetapi mt_rand()juga diunggulkan menggunakan waktu saat ini dan ID proses saat ini. Silakan lihat GENERATE_SEED()di/ext/standard/php_rand.h .
Andrew Moore
53
@ Mike: Silakan, itu ada di sana untuk alasan itu!
Andrew Moore
14
Bagi siapa pun yang berpikir bahwa mereka perlu memodifikasi awal $ string garam dalam fungsi getSalt, itu tidak perlu. $ 2a $ __ adalah bagian dari garam CRYPT_BLOWFISH. Dari dokumen: "hashing Blowfish dengan garam sebagai berikut:" $ 2a $ ", parameter biaya dua digit," $ ", dan 22 digit dari alfabet".
jwinn
18
@MichaelLang: Hal baiknya crypt()adalah peer-review dan diverifikasi. Kode di atas memanggil PHP crypt(), yang memanggil crypt()fungsi POSIX . Semua kode di atas tidak lebih menghasilkan garam acak (yang tidak harus aman secara kriptografi, garam tidak dianggap rahasia) sebelum menelepon crypt(). Mungkin Anda harus melakukan sedikit riset sendiri sebelum memanggil serigala.
Andrew Moore
31
Harap perhatikan bahwa jawaban ini, walaupun bagus, mulai menunjukkan usianya. Kode ini (seperti halnya implementasi PHP bergantung pada crypt()) tunduk pada kerentanan keamanan pra-5.3.7, dan (sangat sedikit) tidak efisien pasca-5.3.7 - rincian masalah yang relevan dapat ditemukan di sini . Harap perhatikan juga bahwa API hashing kata sandi baru ( backwards compat lib ) sekarang merupakan metode yang disukai untuk mengimplementasikan hashing bcrypt kata sandi dalam aplikasi Anda.
DaveRandom
295

Jadi, Anda ingin menggunakan bcrypt? Luar biasa! Namun, seperti bidang kriptografi lainnya, Anda tidak harus melakukannya sendiri. Jika Anda perlu khawatir tentang hal-hal seperti mengelola kunci, atau menyimpan garam atau menghasilkan angka acak, Anda salah melakukannya.

Alasannya sederhana: sangat mudah untuk mengacaukan bcrypt . Bahkan, jika Anda melihat hampir setiap bagian kode pada halaman ini, Anda akan melihat bahwa itu melanggar setidaknya satu dari masalah umum ini.

Hadapi Itu, Kriptografi sulit.

Biarkan untuk para ahli. Biarkan untuk orang-orang yang tugasnya memelihara perpustakaan-perpustakaan ini. Jika Anda perlu mengambil keputusan, Anda salah melakukannya.

Sebagai gantinya, cukup gunakan perpustakaan. Ada beberapa tergantung pada kebutuhan Anda.

Perpustakaan

Berikut ini adalah rincian dari beberapa API yang lebih umum.

PHP 5.5 API - (Tersedia untuk 5.3.7+)

Mulai di PHP 5.5, API baru untuk hashing passwords sedang diperkenalkan. Ada juga perpustakaan kompatibilitas shim yang dikelola (oleh saya) untuk 5.3.7+. Ini memiliki manfaat menjadi peer-review dan implementasi yang mudah digunakan.

function register($username, $password) {
    $hash = password_hash($password, PASSWORD_BCRYPT);
    save($username, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    if (password_verify($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Sungguh, ini ditujukan untuk menjadi sangat sederhana.

Sumber:

Zend \ Crypt \ Password \ Bcrypt (5.3.2+)

Ini adalah API lain yang mirip dengan PHP 5.5 satu, dan melakukan tujuan yang sama.

function register($username, $password) {
    $bcrypt = new Zend\Crypt\Password\Bcrypt();
    $hash = $bcrypt->create($password);
    save($user, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    $bcrypt = new Zend\Crypt\Password\Bcrypt();
    if ($bcrypt->verify($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Sumber:

PasswordLib

Ini adalah pendekatan yang sedikit berbeda untuk hashing kata sandi. Daripada hanya mendukung bcrypt, PasswordLib mendukung sejumlah besar algoritma hashing. Ini terutama berguna dalam konteks di mana Anda perlu mendukung kompatibilitas dengan sistem lama dan berbeda yang mungkin berada di luar kendali Anda. Ini mendukung sejumlah besar algoritma hashing. Dan didukung 5.3.2+

function register($username, $password) {
    $lib = new PasswordLib\PasswordLib();
    $hash = $lib->createPasswordHash($password, '$2y$', array('cost' => 12));
    save($user, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    $lib = new PasswordLib\PasswordLib();
    if ($lib->verifyPasswordHash($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Referensi:

  • Kode Sumber / Dokumentasi: GitHub

PHPASS

Ini adalah lapisan yang mendukung bcrypt, tetapi juga mendukung algoritma yang cukup kuat yang berguna jika Anda tidak memiliki akses ke PHP> = 5.3.2 ... Ini sebenarnya mendukung PHP 3.0+ (walaupun tidak dengan bcrypt).

function register($username, $password) {
    $phpass = new PasswordHash(12, false);
    $hash = $phpass->HashPassword($password);
    save($user, $hash);
}

function login($username, $password) {
    $hash = loadHashByUsername($username);
    $phpass = new PasswordHash(12, false);
    if ($phpass->CheckPassword($password, $hash)) {
        //login
    } else {
        // failure
    }
}

Sumber daya

Catatan: Jangan gunakan alternatif PHPASS yang tidak di-host di openwall, itu adalah proyek yang berbeda !!!

Tentang BCrypt

Jika Anda perhatikan, setiap perpustakaan ini mengembalikan satu string. Itu karena cara BCrypt bekerja secara internal. Dan ada banyak jawaban tentang hal itu. Berikut adalah pilihan yang saya tulis, yang tidak akan saya salin / tempel di sini, tetapi tautkan ke:

Bungkus

Ada banyak pilihan berbeda. Yang Anda pilih terserah Anda. Namun, saya akan SANGAT menyarankan agar Anda menggunakan salah satu perpustakaan di atas untuk menangani ini untuk Anda.

Sekali lagi, jika Anda menggunakan crypt()secara langsung, Anda mungkin melakukan sesuatu yang salah. Jika kode Anda menggunakan hash()(atau md5()atau sha1()) secara langsung, Anda hampir pasti melakukan sesuatu yang salah.

Cukup gunakan perpustakaan ...

ircmaxell
sumber
7
Garam harus dibuat secara acak, tetapi tidak harus berasal dari sumber acak yang aman. Garam bukan rahasia . Mampu menebak garam berikutnya tidak memiliki dampak keamanan nyata; selama mereka berasal dari kumpulan data yang cukup besar untuk menghasilkan garam yang berbeda untuk setiap kata sandi yang dikodekan, Anda baik-baik saja. Ingat, garam ada di sana untuk mencegah penggunaan meja pelangi jika hash Anda jatuh ke tangan yang buruk. Mereka bukan rahasia.
Andrew Moore
7
@AndrewMoore benar sekali! Namun, garam harus memiliki cukup entropi untuk menjadi unik secara statistik. Tidak hanya di aplikasi Anda, tetapi di semua aplikasi. Jadi mt_rand()memiliki periode yang cukup tinggi, tetapi nilai benih hanya 32 bit. Jadi menggunakan mt_rand()secara efektif membatasi Anda hanya untuk 32 bit entropi. Yang berkat Masalah Ulang Tahun berarti bahwa Anda memiliki kemungkinan tabrakan 50% hanya pada 7k garam yang dihasilkan (secara global). Karena bcryptmenerima 128 bit garam, lebih baik menggunakan sumber yang dapat memasok semua 128 bit ;-). (pada 128 bit, kemungkinan tabrakan 50% terjadi pada hash 2e19) ...
ircmaxell
1
@ircmaxell: Pegang "kumpulan data yang cukup besar". Namun sumber Anda tidak harus menjadi sumber entropi SANGAT TINGGI, cukup tinggi untuk 128 bit. Namun, jika Anda telah kehabisan semua sumber yang tersedia (tidak memiliki OpenSSL, dll ...) dan satu-satunya fallback Anda adalah mt_rand (), itu masih lebih baik daripada alternatifnya (yaitu rand ()).
Andrew Moore
4
@AndrewMoore: tentu saja. Tidak membantah itu. Hanya itu mt_randdan uniqid(dan karenanya lcg_valuedan rand) bukanlah pilihan pertama ...
ircmaxell
1
ircmaxell, terima kasih banyak atas pustaka password_compat untuk 5.3.xx, kami belum membutuhkan ini sebelumnya tetapi sekarang kami melakukannya, pada server php 5.3.xx, dan terima kasih atas saran Anda yang jelas untuk tidak mencoba melakukan logika ini diri.
Lizardx
47

Anda akan mendapatkan banyak informasi dalam Cukup Dengan Tabel Pelangi: Yang Perlu Anda Ketahui Tentang Skema Kata Sandi Aman atau kerangka kerja hashing kata sandi PHP Portabel .

Tujuannya adalah untuk hash kata sandi dengan sesuatu yang lambat, sehingga seseorang yang mendapatkan basis data kata sandi Anda akan mati karena berusaha memaksakannya (penundaan 10 ms untuk memeriksa kata sandi tidak ada artinya bagi Anda, banyak bagi seseorang yang mencoba memaksakannya). Bcrypt lambat dan dapat digunakan dengan parameter untuk memilih seberapa lambat itu.

Arkh
sumber
7
Menerapkan apa pun yang Anda inginkan, pengguna akan berhasil mengacau dan menggunakan kata sandi yang sama pada banyak hal. Jadi Anda harus melindunginya sebanyak mungkin atau mengimplementasikan sesuatu yang memungkinkan Anda tidak perlu menyimpan kata sandi (SSO, openID, dll.).
Arkh
41
Tidak. Kata sandi digunakan untuk melindungi terhadap satu serangan: seseorang mencuri basis data Anda dan ingin mendapatkan info masuk + kata sandi yang jelas.
Arkh
4
@Josh K. Saya mendorong Anda untuk mencoba memecahkan beberapa kata sandi sederhana setelah membuatnya melalui phpass yang disetel sehingga dibutuhkan antara 1ms dan 10ms untuk menghitungnya di server web Anda.
Arkh
3
Sepakat. Tetapi jenis pengguna yang akan menggunakan qwerty sebagai kata sandi juga jenis pengguna yang akan menandai siapa pun yang rumit di mana ia (dan penyerang) dapat dengan mudah membacanya. Apa yang dicapai dengan menggunakan bcrypt adalah bahwa ketika db Anda go public melawan kehendak Anda, akan lebih sulit untuk menjangkau pengguna yang memiliki beberapa kata sandi seperti ^ | $$ & ZL6- £ daripada jika Anda menggunakan sha512 dalam satu pass.
Arkh
4
@coreyward perlu dicatat bahwa melakukan itu lebih berbahaya daripada tidak memblokir sama sekali; yang mudah dianggap sebagai vektor "penolakan layanan". Cukup mulai mengirim spam login buruk pada akun yang dikenal dan Anda dapat mengganggu banyak pengguna dengan sangat mudah. Lebih baik untuk tarpit (menunda) penyerang daripada langsung menolak akses, terutama jika itu adalah pelanggan yang membayar.
damianb
36

Anda dapat membuat hash satu arah dengan bcrypt menggunakan crypt()fungsi PHP dan mengirimkan garam Blowfish yang sesuai. Yang paling penting dari keseluruhan persamaan adalah bahwa A) algoritme belum dikompromikan dan B) Anda benar garam setiap kata sandi . Jangan gunakan garam aplikasi-lebar; yang membuka seluruh aplikasi Anda untuk menyerang dari satu set tabel Rainbow.

PHP - Fungsi Crypt

intinya
sumber
4
Ini adalah pendekatan yang tepat - gunakan crypt()fungsi PHP , yang mendukung beberapa fungsi hashing kata sandi yang berbeda. Pastikan Anda tidak menggunakan CRYPT_STD_DESatau CRYPT_EXT_DES- salah satu dari jenis yang didukung lainnya baik-baik saja (dan termasuk bcrypt, dengan nama CRYPT_BLOWFISH).
caf
4
SHA memang memiliki parameter biaya juga, melalui opsi 'putaran'. Saat menggunakan itu, saya juga tidak melihat alasan untuk memilih bcrypt.
Pieter Ennes
3
Sebenarnya, satu SHA-1 (atau MD5) dari sebuah kata sandi masih dengan mudah dapat di-brute-force, dengan atau tanpa garam (garam membantu melawan tabel pelangi, bukan melawan brute-forcing). Gunakan bcrypt.
Paŭlo Ebermann
Saya merasa terganggu bahwa semua orang tampaknya mengatakan "bcrypt" ketika itu berarti php's crypt ().
Sliq
3
@Panique Why? Algoritma ini disebut bcrypt . cryptmemperlihatkan beberapa hash kata sandi, dengan bcrypt yang sesuai dengan CRYPT_BLOWFISHkonstanta. Bcrypt saat ini merupakan algoritma terkuat yang didukung oleh cryptdan beberapa lainnya yang didukungnya cukup lemah.
CodesInChaos
34

Sunting: 2013.01.15 - Jika server Anda akan mendukungnya, gunakan solusi martinstoeckli sebagai gantinya.


Semua orang ingin membuat ini lebih rumit daripada itu. Fungsi crypt () melakukan sebagian besar pekerjaan.

function blowfishCrypt($password,$cost)
{
    $chars='./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    $salt=sprintf('$2y$%02d$',$cost);
//For PHP < PHP 5.3.7 use this instead
//    $salt=sprintf('$2a$%02d$',$cost);
    //Create a 22 character salt -edit- 2013.01.15 - replaced rand with mt_rand
    mt_srand();
    for($i=0;$i<22;$i++) $salt.=$chars[mt_rand(0,63)];
    return crypt($password,$salt);
}

Contoh:

$hash=blowfishCrypt('password',10); //This creates the hash
$hash=blowfishCrypt('password',12); //This creates a more secure hash
if(crypt('password',$hash)==$hash){ /*ok*/ } //This checks a password

Saya tahu itu harus jelas, tapi tolong jangan gunakan 'kata sandi' sebagai kata sandi Anda.

Jon Hulka
sumber
3
Penciptaan garam dapat ditingkatkan (gunakan sumber acak dari OS), jika tidak, itu terlihat baik bagi saya. Untuk versi PHP yang lebih baru lebih baik digunakan 2ydaripada 2a.
martinstoeckli
gunakan mcrypt_create_iv($size, MCRYPT_DEV_URANDOM)sebagai sumber garam.
CodesInChaos
Saya akan melihat lebih dekat pada mcrypt_create_iv () ketika saya mendapatkan momen, jika tidak ada yang lain itu akan sedikit meningkatkan kinerja.
Jon Hulka
2
Tambahkan pengkodean Base64 dan terjemahkan ke penggunaan alfabet kustom bcrypt. mcrypt_create_iv(17, MCRYPT_DEV_URANDOM), str_replace('+', '.', base64_encode($rawSalt)),$salt = substr($salt, 0, 22);
CodesInChaos
1
@JonHulka - Lihatlah paket kompatibilitas PHP [Line 127], ini adalah implementasi yang mudah.
martinstoeckli
29

Versi 5.5 dari PHP akan memiliki dukungan bawaan untuk BCrypt, fungsi password_hash()dan password_verify(). Sebenarnya ini hanya pembungkus di sekitar fungsicrypt() , dan akan membuatnya lebih mudah untuk menggunakannya dengan benar. Ini menangani pembuatan garam acak yang aman, dan memberikan nilai default yang baik.

Cara termudah untuk menggunakan fungsi ini adalah:

$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
$isPasswordCorrect = password_verify($password, $existingHashFromDb);

Kode ini akan memotong kata sandi dengan BCrypt (algoritma 2y ), menghasilkan garam acak dari sumber acak OS, dan menggunakan parameter biaya default (saat ini 10). Baris kedua memeriksa, apakah pengguna memasukkan kata sandi cocok dengan nilai hash yang sudah tersimpan.

Jika Anda ingin mengubah parameter biaya, Anda dapat melakukannya seperti ini, meningkatkan parameter biaya sebesar 1, menggandakan waktu yang dibutuhkan untuk menghitung nilai hash:

$hash = password_hash($password, PASSWORD_BCRYPT, array("cost" => 11));

Berbeda dengan "cost"parameter, yang terbaik adalah menghilangkan"salt" parameter, karena fungsi sudah melakukan yang terbaik untuk membuat garam yang aman secara kriptografis.

Untuk PHP versi 5.3.7 dan yang lebih baru, ada paket kompatibilitas , dari penulis yang sama yang membuat password_hash()fungsi. Untuk versi PHP sebelum 5.3.7 tidak ada dukungan untuk crypt()dengan 2y, algoritma BCrypt unicode safe. Orang bisa menggantinya dengan 2a, yang merupakan alternatif terbaik untuk versi PHP sebelumnya.

martinstoeckli
sumber
3
Setelah saya membaca ini, pikiran pertama saya adalah "bagaimana Anda menyimpan garam yang dihasilkan"? Setelah melihat-lihat dokumen, fungsi password_hash () akhirnya menghasilkan string yang menyimpan metode enkripsi, garam, dan hash yang dihasilkan. Jadi, ia hanya menyimpan semua yang dibutuhkan dalam satu string agar fungsi password_verify () berfungsi. Hanya ingin menyebutkan ini karena dapat membantu orang lain ketika mereka melihat ini.
jzimmerman2011
@ jzimmerman2011 - Tepat, di jawaban lain saya mencoba menjelaskan format penyimpanan ini dengan sebuah contoh.
martinstoeckli
7

Pemikiran saat ini: hash harus paling lambat tersedia, bukan yang tercepat. Ini menekan meja pelangi serangan .

Juga terkait, tetapi pencegahan: Penyerang tidak boleh memiliki akses tak terbatas ke layar login Anda. Untuk mencegahnya: Siapkan tabel pelacakan alamat IP yang merekam setiap klik bersama dengan URI. Jika lebih dari 5 upaya untuk masuk berasal dari alamat IP yang sama dalam periode lima menit, blokir dengan penjelasan. Pendekatan sekunder adalah memiliki skema kata sandi dua tingkat, seperti yang dilakukan bank. Menempatkan lock-out untuk kegagalan pada pass kedua meningkatkan keamanan.

Rangkuman: memperlambat penyerang dengan menggunakan fungsi hash yang menghabiskan waktu. Juga, blokir pada terlalu banyak akses ke login Anda, dan tambahkan tingkat kata sandi kedua.

FYA
sumber
Saya pikir mereka berasumsi bahwa penyerang telah berhasil mencuri DB saya melalui beberapa cara lain, dan sekarang sedang mencoba mengeluarkan kata sandi untuk mencobanya di paypal atau sesuatu.
Vilx-
4
Setengah jalan hingga 2012 dan jawaban ini masih miring, bagaimana algoritma hashing lambat mencegah serangan tabel pelangi? Saya pikir garam rentang byte acak lakukan? Saya selalu berpikir kecepatan algoritma hashing menentukan berapa banyak iterasi yang dapat mereka kirimkan terhadap hash yang mereka dapatkan dari Anda dalam jumlah waktu tertentu. JANGAN PERNAH MEMBLOKIR PENGGUNA MENGGUNAKAN LOGIN GAGAL percayalah, pengguna Anda akan muak, sering di beberapa situs saya perlu login dekat 5 kali kadang-kadang lebih sebelum saya ingat kata sandi saya untuk itu. Tingkat kedua juga tidak berfungsi, dua langkah auth dengan kode ponsel bisa.
Sammaye
1
@Sammaye Saya setuju dengan ini sampai titik tertentu. Saya menyiapkan blok pada 5 upaya login yang gagal, sebelum menaikkannya dengan cepat ke 7, lalu 10 sekarang menjadi 20. Tidak ada pengguna normal yang seharusnya memiliki 20 upaya login gagal tetapi cukup rendah untuk dengan mudah menghentikan serangan brute force
Bruce Aldridge
@BruceAldridge Saya pribadi akan berpikir akan lebih baik untuk membuat skrip Anda berhenti untuk waktu yang acak setelah mengatakan, 7 gagal login dan menunjukkan captcha daripada memblokir. Memblokir adalah langkah yang sangat agresif untuk dilakukan.
Sammaye
1
@Sammaye Saya setuju blok permanen buruk. Saya mengacu pada blok sementara yang meningkat dengan jumlah upaya yang gagal.
Bruce Aldridge
7

Inilah jawaban yang diperbarui untuk pertanyaan lama ini!

Cara yang tepat untuk kata sandi hash di PHP sejak 5.5 adalah dengan password_hash(), dan cara yang benar untuk memverifikasi mereka dengan password_verify(), dan ini masih berlaku di PHP 8.0. Fungsi-fungsi ini menggunakan hash bcrypt secara default, tetapi algoritma yang lebih kuat lainnya telah ditambahkan. Anda dapat mengubah faktor kerja (seberapa efektif enkripsi "kuat") melalui password_hashparameter.

Namun, meskipun masih cukup kuat, bcrypt tidak lagi dianggap canggih ; satu set algoritma hash kata sandi yang lebih baik telah tiba disebut Argon2 , dengan Argon2i, Argon2d, dan varian Argon2id. Perbedaan di antara mereka (seperti yang dijelaskan di sini ):

Argon2 memiliki satu varian utama: Argon2id, dan dua varian tambahan: Argon2d dan Argon2i. Argon2d menggunakan akses memori yang bergantung pada data, yang membuatnya cocok untuk aplikasi cryptocurrency dan proof-of-work tanpa ancaman dari serangan timing-channel samping. Argon2i menggunakan akses memori data-independen, yang lebih disukai untuk hashing kata sandi dan derivasi kunci berbasis kata sandi. Argon2id bekerja sebagai Argon2i untuk paruh pertama dari iterasi pertama atas memori, dan sebagai Argon2d untuk sisanya, sehingga memberikan perlindungan serangan saluran-samping dan penghematan biaya gaya-kasar karena pengorbanan waktu-memori.

Dukungan Argon2i ditambahkan dalam PHP 7.2, dan Anda memintanya seperti ini:

$hash = password_hash('mypassword', PASSWORD_ARGON2I);

dan dukungan Argon2id ditambahkan dalam PHP 7.3:

$hash = password_hash('mypassword', PASSWORD_ARGON2ID);

Tidak ada perubahan yang diperlukan untuk memverifikasi kata sandi karena string hash yang dihasilkan berisi informasi tentang algoritma, garam, dan faktor kerja apa yang digunakan saat itu dibuat.

Secara terpisah (dan agak berlebihan), libsodium (ditambahkan dalam PHP 7.2) juga menyediakan hashing Argon2 melalui sodium_crypto_pwhash_str ()dan sodium_crypto_pwhash_str_verify()fungsi, yang bekerja sangat mirip dengan PHP bawaan . Salah satu alasan yang mungkin untuk menggunakan ini adalah bahwa PHP kadang-kadang dapat dikompilasi tanpa libargon2, yang membuat algoritma Argon2 tidak tersedia untuk fungsi password_hash; PHP 7.2 dan lebih tinggi harus selalu mengaktifkan libsodium, tetapi mungkin tidak - tetapi setidaknya ada dua cara yang bisa Anda dapatkan pada algoritma itu. Inilah cara Anda dapat membuat hash Argon2id dengan libsodium (bahkan dalam PHP 7.2, yang sebaliknya tidak memiliki dukungan Argon2id)):

$hash = sodium_crypto_pwhash_str(
    'mypassword',
    SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE,
    SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE
);

Perhatikan bahwa tidak memungkinkan Anda menentukan garam secara manual; ini adalah bagian dari etos libsodium - jangan izinkan pengguna mengatur params ke nilai yang dapat membahayakan keamanan - misalnya tidak ada yang mencegah Anda mengirimkan string garam kosong ke password_hashfungsi PHP ; libsodium tidak membiarkan Anda melakukan hal konyol itu!

Sinkron
sumber
4

Untuk kata sandi OAuth 2 :

$bcrypt = new \Zend\Crypt\Password\Bcrypt;
$bcrypt->create("youpasswordhere", 10)
Shemeer M Ali
sumber
1

Seperti yang kita semua tahu menyimpan kata sandi dalam teks yang jelas dalam database tidak aman. bcrypt adalah teknik kata sandi hashing. Ini digunakan untuk membangun keamanan kata sandi. salah satu fungsi bcrypt yang luar biasa adalah menyelamatkan kita dari peretas. Ia digunakan untuk melindungi kata sandi dari serangan peretasan karena kata sandi disimpan dalam bentuk bcrypt.

fungsi password_hash () digunakan untuk membuat hash kata sandi baru. Ini menggunakan algoritma hashing yang kuat & tangguh. Fungsi password_hash () sangat kompatibel dengan fungsi crypt (). Oleh karena itu, hash kata sandi yang dibuat oleh crypt () dapat digunakan dengan password_hash () dan sebaliknya. Fungsi password_verify () dan password_hash () hanya pembungkus di sekitar fungsi crypt (), dan mereka membuatnya lebih mudah untuk menggunakannya secara akurat.

SINTAKSIS

string password_hash($password , $algo , $options)

Algoritma berikut saat ini didukung oleh fungsi password_hash ():

PASSWORD_DEFAULT PASSWORD_BCRYPT PASSWORD_ARGON2I PASSWORD_ARGON2ID

Parameter: Fungsi ini menerima tiga parameter seperti yang disebutkan di atas dan dijelaskan di bawah:

kata sandi : Ini menyimpan kata sandi pengguna. algo : Ini adalah konstanta algoritma kata sandi yang digunakan terus-menerus sambil menunjukkan algoritma yang akan digunakan ketika hashing kata sandi terjadi. Opsi : Ini adalah array asosiatif, yang berisi opsi. Jika ini dihapus dan tidak termasuk, garam acak akan digunakan, dan pemanfaatan biaya standar akan terjadi. Kembali Nilai : Ini mengembalikan kata sandi hash pada keberhasilan atau Salah pada kegagalan.

Contoh :

Input : echo password_hash("GFG@123", PASSWORD_DEFAULT); Output : $2y$10$.vGA19Jh8YrwSJFDodbfoHJIOFH)DfhuofGv3Fykk1a

Program-program di bawah ini menggambarkan fungsi password_hash () dalam PHP:

<?php echo password_hash("GFG@123", PASSWORD_DEFAULT); ?>

KELUARAN

$2y$10$Z166W1fBdsLcXPVQVfPw/uRq1ueWMA6sLt9bmdUFz9AmOGLdM393G

Nayab Muhammad
sumber