it-swarm-vi.com

Làm thế nào để bạn sử dụng bcrypt để băm mật khẩu trong PHP?

Thỉnh thoảng tôi nghe thấy lời khuyên "Sử dụng bcrypt để lưu trữ mật khẩu trong PHP, quy tắc bcrypt".

Nhưng bcrypt là gì? PHP không cung cấp bất kỳ chức năng nào như vậy, Wikipedia bập bẹ về một tiện ích mã hóa tệp và các tìm kiếm trên Web chỉ tiết lộ một vài triển khai của Blowfish bằng các ngôn ngữ khác nhau. Bây giờ Blowfish cũng có sẵn trong PHP thông qua mcrypt, nhưng làm thế nào để giúp lưu trữ mật khẩu? Blowfish là một mật mã mục đích chung, nó hoạt động theo hai cách. Nếu nó có thể được mã hóa, nó có thể được giải mã. Mật khẩu cần một hàm băm một chiều.

Giải thích là gì?

1201
Vilx-

bcrypt là một thuật toán băm có thể mở rộng bằng phần cứng (thông qua số vòng có thể định cấu hình). Sự chậm chạp và nhiều vòng của nó đảm bảo rằng kẻ tấn công phải triển khai số tiền lớn và phần cứng để có thể bẻ khóa mật khẩu của bạn. Thêm vào đó mỗi mật khẩu muối (bcrypt YÊU CẦU muối) và bạn có thể chắc chắn rằng một cuộc tấn công hầu như không khả thi nếu không có số tiền hoặc phần cứng lố bịch.

bcrypt sử dụng thuật toán Eksblowfish để băm mật khẩu. Trong khi pha mã hóa của EksblowfishBlowfish hoàn toàn giống nhau, giai đoạn lịch biểu chính của Eksblowfish đảm bảo rằng mọi trạng thái tiếp theo phụ thuộc vào cả muối và khóa (người dùng mật khẩu) và không có trạng thái nào có thể được tính toán trước mà không có kiến ​​thức về cả hai. Do sự khác biệt chính này, bcrypt là thuật toán băm một chiều. Bạn không thể truy xuất mật khẩu văn bản đơn giản mà không biết muối, làm tròn và khóa (mật khẩu). [ Nguồn ]

Cách sử dụng bcrypt:

Sử dụng PHP> = 5,5-DEV

Các hàm băm mật khẩu hiện đã được tích hợp trực tiếp vào PHP> = 5.5 . Bây giờ bạn có thể sử dụng password_hash() để tạo băm bcrypt của bất kỳ mật khẩu nào:

<?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

Để xác minh mật khẩu do người dùng cung cấp đối với hàm băm hiện có, bạn có thể sử dụng password_verify() như vậy:

<?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.';
}

Sử dụng PHP> = 5.3.7, <5.5-DEV (cũng là RedHat PHP> = 5.3.3)

Có một thư viện tương thích on GitHub được tạo dựa trên mã nguồn của các hàm trên được viết bằng C, cung cấp cùng chức năng. Khi thư viện tương thích được cài đặt, việc sử dụng cũng giống như trên (trừ ký hiệu mảng tốc ký nếu bạn vẫn ở nhánh 5.3.x).

Sử dụng PHP <5.3.7 (ĐÃ ĐỔI)

Bạn có thể sử dụng hàm crypt() để tạo băm mã hóa bcrypt của chuỗi đầu vào. Lớp này có thể tự động tạo ra muối và xác minh các giá trị băm hiện có đối với đầu vào. Nếu bạn đang sử dụng phiên bản PHP cao hơn hoặc bằng 5.3.7, bạn nên sử dụng hàm dựng sẵn hoặc thư viện compat . Sự thay thế này chỉ được cung cấp cho mục đích lịch sử.

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;
  }
}

Bạn có thể sử dụng mã này như thế này:

$bcrypt = new Bcrypt(15);

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

Ngoài ra, bạn cũng có thể sử dụng Portable PHP Khung băm .

1025
Andrew Moore

Vì vậy, bạn muốn sử dụng bcrypt? Tuyệt vời! Tuy nhiên, giống như các lĩnh vực khác của mật mã, bạn không nên tự mình làm điều đó. Nếu bạn cần lo lắng về bất cứ điều gì như quản lý khóa, hoặc lưu trữ muối hoặc tạo số ngẫu nhiên, bạn đã làm sai.

Lý do rất đơn giản: thật dễ dàng để làm hỏng bcrypt . Trên thực tế, nếu bạn nhìn vào hầu hết mọi đoạn mã trên trang này, bạn sẽ nhận thấy rằng nó vi phạm ít nhất một trong những vấn đề phổ biến này.

Đối mặt với nó, Mật mã là khó.

Để lại cho các chuyên gia. Để lại cho những người làm việc đó là duy trì các thư viện này. Nếu bạn cần đưa ra quyết định, bạn đang làm sai.

Thay vào đó, chỉ cần sử dụng một thư viện. Một số tồn tại tùy thuộc vào yêu cầu của bạn.

Thư viện

Dưới đây là bảng phân tích một số API phổ biến hơn.

API PHP 5.5 - (Có sẵn cho 5.3.7+)

Bắt đầu từ PHP 5.5, một API mới để băm mật khẩu đang được giới thiệu. Ngoài ra còn có một thư viện tương thích shim được duy trì (bởi tôi) cho 5.3.7+. Điều này có lợi ích là được đánh giá ngang hàng và đơn giản để sử dụng triển khai.

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
    }
}

Thực sự, nó nhằm mục đích cực kỳ đơn giản.

Tài nguyên:

Zend\Crypt\Password\Bcrypt (5.3.2+)

Đây là một API khác tương tự như PHP 5.5, và có mục đích tương tự.

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
    }
}

Tài nguyên:

Mật khẩu

Đây là một cách tiếp cận hơi khác nhau để băm mật khẩu. Thay vì chỉ đơn giản là hỗ trợ bcrypt, PasswordLib hỗ trợ một số lượng lớn các thuật toán băm. Nó chủ yếu hữu ích trong các bối cảnh mà bạn cần hỗ trợ khả năng tương thích với các hệ thống cũ và khác biệt có thể nằm ngoài tầm kiểm soát của bạn. Nó hỗ trợ một số lượng lớn các thuật toán băm. Và được hỗ trợ 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
    }
}

Tài liệu tham khảo:

  • Mã nguồn/Tài liệu: GitHub

PHPASS

Đây là lớp hỗ trợ bcrypt, nhưng cũng hỗ trợ thuật toán khá mạnh, hữu ích nếu bạn không có quyền truy cập vào PHP> = 5.3.2 ... Nó thực sự hỗ trợ PHP 3.0+ (mặc dù không phải với 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
    }
}

Tài nguyên

Lưu ý: Đừng sử dụng các lựa chọn thay thế PHPASS không được lưu trữ trên openwall, chúng là các dự án khác nhau !!!

Giới thiệu về BCrypt

Nếu bạn để ý, mỗi một trong những thư viện này sẽ trả về một chuỗi. Đó là vì cách BCrypt hoạt động nội bộ. Và có một TON câu trả lời về điều đó. Đây là một lựa chọn mà tôi đã viết, tôi sẽ không sao chép/dán ở đây, nhưng liên kết đến:

Gói lại

Có nhiều sự lựa chọn khác nhau. Mà bạn chọn là tùy thuộc vào bạn. Tuy nhiên, tôi sẽRẤTkhuyên bạn nên sử dụng một trong các thư viện trên để xử lý việc này cho bạn.

Một lần nữa, nếu bạn đang sử dụng trực tiếp crypt(), có lẽ bạn đã làm sai điều gì đó. Nếu mã của bạn đang sử dụng trực tiếp hash() (hoặc md5() hoặc sha1()), bạn gần như chắc chắn đã làm sai.

Chỉ cần sử dụng một thư viện ...

283
ircmaxell

Bạn sẽ nhận được rất nhiều thông tin trongĐủ với Bảng cầu vồng: Những điều bạn cần biết về Lược đồ mật khẩu an toànhoặcDi động PHP băm mật khẩu khung.

Mục tiêu là băm mật khẩu với một cái gì đó chậm, vì vậy ai đó nhận được cơ sở dữ liệu mật khẩu của bạn sẽ chết vì cố gắng ép buộc nó (một sự chậm trễ 10 ms để kiểm tra mật khẩu là không có gì cho bạn, rất nhiều cho ai đó đang cố gắng ép buộc nó). Bcrypt chậm và có thể được sử dụng với một tham số để chọn mức độ chậm của nó.

46
Arkh

Bạn có thể tạo hàm băm một chiều bằng bcrypt bằng hàm crypt() của PHP và chuyển vào một loại muối Thổi phù hợp. Điều quan trọng nhất của toàn bộ phương trình là A) thuật toán chưa bị xâm phạm và B) bạn muối đúng từng mật khẩu . Đừng sử dụng muối trên toàn ứng dụng; mở ra toàn bộ ứng dụng của bạn để tấn công từ một tập hợp các bảng Rainbow.

PHP - Chức năng mã hóa

35
coreyward

Chỉnh sửa: 2013.01.15 - Nếu máy chủ của bạn sẽ hỗ trợ, hãy sử dụng giải pháp của martinstoeckli thay vào đó.


Mọi người đều muốn làm cho điều này phức tạp hơn nó. Hàm crypt () thực hiện hầu hết công việc.

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);
}

Thí dụ:

$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

Tôi biết điều đó là hiển nhiên, nhưng vui lòng không sử dụng 'mật khẩu' làm mật khẩu của bạn.

33
Jon Hulka

Phiên bản 5.5 của PHP sẽ có hỗ trợ tích hợp cho BCrypt, các chức năng password_hash()password_verify() . Trên thực tế đây chỉ là các hàm bao quanh hàm crypt() , và sẽ giúp sử dụng chính xác dễ dàng hơn. Nó quan tâm đến việc tạo ra một loại muối ngẫu nhiên an toàn và cung cấp các giá trị mặc định tốt.

Cách dễ nhất để sử dụng chức năng này sẽ là:

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

Mã này sẽ băm mật khẩu bằng BCrypt (thuật toán 2y), tạo ra một muối ngẫu nhiên từ nguồn ngẫu nhiên của hệ điều hành và sử dụng tham số chi phí mặc định (tại thời điểm này là 10). Dòng thứ hai kiểm tra, nếu người dùng nhập mật khẩu khớp với giá trị băm đã được lưu trữ.

Nếu bạn muốn thay đổi tham số chi phí, bạn có thể làm như vậy, tăng tham số chi phí lên 1, tăng gấp đôi thời gian cần thiết để tính giá trị băm:

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

Ngược lại với tham số "cost", tốt nhất là bỏ qua tham số "salt", vì hàm này đã làm hết sức để tạo ra một loại muối an toàn bằng mật mã.

Đối với PHP phiên bản 5.3.7 trở lên, tồn tại một gói tương thích , từ cùng một tác giả đã tạo ra hàm password_hash(). Đối với các phiên bản PHP trước 5.3.7, không có hỗ trợ nào cho crypt() với 2y, thuật toán BCrypt an toàn unicode. Người ta có thể thay thế nó bằng 2a, đây là sự thay thế tốt nhất cho các phiên bản PHP trước đó.

27
martinstoeckli

Một cách khác là sử dụng tiền điện tử, được thiết kế đặc biệt để vượt trội hơn bcrypt bởi Colin Percival trong bài viết của anh ấy . Có một phần mở rộng scrypt PHP trong PECL . Lý tưởng nhất là thuật toán này sẽ được đưa vào PHP để nó có thể được chỉ định cho các hàm password_ * (lý tưởng là "PASSWORD_SCRYPT"), nhưng chưa có.

6
Synchro

Suy nghĩ hiện tại: băm nên là chậm nhất có sẵn, không phải là nhanh nhất có thể. Điều này triệt tiêu Bảng cầu vồng tấn công.

Cũng liên quan, nhưng phòng ngừa: Kẻ tấn công không bao giờ có quyền truy cập không giới hạn vào màn hình đăng nhập của bạn. Để ngăn chặn điều đó: Thiết lập bảng theo dõi địa chỉ IP ghi lại mọi lần truy cập cùng với URI. Nếu có hơn 5 lần đăng nhập đến từ cùng một địa chỉ IP trong bất kỳ khoảng thời gian năm phút nào, hãy chặn lời giải thích. Một cách tiếp cận thứ cấp là có một sơ đồ mật khẩu hai tầng, giống như các ngân hàng. Đặt khóa cho các lỗi trên đường chuyền thứ hai giúp tăng cường bảo mật.

Tóm tắt: làm chậm kẻ tấn công bằng cách sử dụng các hàm băm tốn thời gian. Ngoài ra, chặn quá nhiều quyền truy cập vào thông tin đăng nhập của bạn và thêm một lớp mật khẩu thứ hai.

6
FYA

Đối với OAuth 2 mật khẩu:

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