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


1255

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 là bcryptgì? 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ì?


13
Câu hỏi này đã được giải quyết trước đây và đề xuất của họ về việc sử dụng một thư viện tiêu chuẩn là tuyệt vời. Bảo mật là một vấn đề phức tạp và bằng cách sử dụng một gói được thiết kế bởi một người biết những gì họ đang làm, bạn chỉ đang giúp mình.
Eykanal

59
@eykanal - trang đó thậm chí không đề cập đến bcrypt, ít giải thích nó là gì .
Vilx-

8
@eykanal - Tôi không hỏi giải thích về cách thức hoạt động của nó. Tôi chỉ muốn biết nó là . Bởi vì bất cứ điều gì tôi có thể khai thác trên mạng theo từ khóa "bcrypt", không thể được sử dụng để băm mật khẩu. Không trực tiếp dù sao, và không phải trong PHP. OK, đến bây giờ tôi hiểu rằng đó thực sự là gói "phpass" sử dụng blowfish để mã hóa mật khẩu của bạn bằng một khóa có nguồn gốc từ mật khẩu của bạn (thực chất là mã hóa mật khẩu bằng chính nó). Nhưng gọi nó là "bcrypt" là sai lệch nghiêm trọng, và đó là điều tôi muốn làm rõ trong câu hỏi này.
Vilx-

3
@Vilx: Tôi đã thêm nhiều thông tin hơn về lý do tại sao bcryptthuật toán băm một chiều so với sơ đồ mã hóa trong câu trả lời của tôi . Có toàn bộ quan niệm sai lầm bcryptnày chỉ là Blowfish khi trên thực tế nó có một lịch trình khóa hoàn toàn khác nhau, đảm bảo rằng văn bản thuần túy không thể được phục hồi từ văn bản mật mã mà không biết trạng thái ban đầu của mật mã (muối, vòng, khóa).
Andrew Moore

1
Cũng xem khung băm mật khẩu PHP di động (PHPass) của Openwall. Nó cứng lại trước một số cuộc tấn công phổ biến vào mật khẩu người dùng.
jww

Câu trả lời:


1065

bcryptlà một thuật toán băm có thể mở rộng với 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 muối mật khẩu ( muốibcrypt YÊU CẦU) 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.

bcryptsử dụng thuật toán Eksblowfish để băm mật khẩu. Mặc dù giai đoạn mã hóa của EksblowfishBlowfish hoàn toàn giống nhau, nhưng giai đoạn lịch trình 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 (mật khẩu người dùng) 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, bcryptlà một 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, vòng 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 một bcrypthàm băm 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ư sau:

<?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 trên 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 (TIẾP THEO)

Bạn có thể sử dụng crypt()chức năng để tạo băm 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 Khung băm PHP di động .


7
@ The Wicky Flea: Xin lỗi đã làm bạn thất vọng, nhưng mt_rand()cũng được chọn bằng cách sử dụng thời gian hiện tại và ID quy trình hiện tại. Xin vui lòng xem GENERATE_SEED()trong/ext/standard/php_rand.h .
Andrew Moore

53
@Mike: Hãy tiếp tục, đó là lý do chính xác!
Andrew Moore

14
Đối với bất kỳ ai nghĩ rằng họ cần sửa đổi phần đầu của chuỗi $ salt trong hàm getSalt, điều đó là không cần thiết. $ 2a $ __ là một phần của muối CRYPT_BLOWFISH. Từ các tài liệu: "Băm cá bơn bằng muối như sau:" $ 2a $ ", tham số chi phí gồm hai chữ số," $ "và 22 chữ số từ bảng chữ cái".
jwinn

18
@MichaelLang: Điều tốt crypt()là được đánh giá ngang hàng và xác minh sau đó. Đoạn mã trên gọi PHP crypt(), gọi crypt()hàm POSIX . Tất cả các mã ở trên có nhiều hơn là tạo ra một loại muối ngẫu nhiên (không cần phải bảo mật bằng mật mã, muối không được coi là bí mật) trước khi gọi crypt(). Có lẽ bạn nên tự nghiên cứu một chút trước khi gọi sói.
Andrew Moore

31
Xin lưu ý rằng câu trả lời này, trong khi tốt, đang bắt đầu cho thấy tuổi của nó. Mã này (giống như bất kỳ triển khai PHP nào dựa vào crypt()) phải chịu lỗ hổng bảo mật trước 5.3.7 và (rất ít) không hiệu quả sau 5.3.7 - có thể tìm thấy chi tiết về vấn đề liên quan ở đây . Cũng xin lưu ý rằng API băm mật khẩu mới ( ngược lại compat lib ) hiện là phương pháp ưa thích để thực hiện băm mật khẩu bcrypt trong ứng dụng của bạn.
DaveRandom

295

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 nó cho những người có nhiệm vụ 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à việc thực hiện đánh giá ngang hàng và đơn giản để sử dụng.

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

Người giới thiệu:

  • 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 ý: Khô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 nhận thấy, mỗi một trong những thư viện này 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ẤT khuyê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 crypt()trực tiếp, có lẽ bạn đang làm sai điều gì đó. Nếu mã của bạn đang sử dụng hash()(hoặc md5()hoặc sha1()) trực tiếp, bạn gần như chắc chắn đã làm gì đó sai.

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


7
Muối phải được tạo ngẫu nhiên, tuy nhiên nó không cần đến từ một nguồn ngẫu nhiên an toàn. Muối không phải là một bí mật . Có thể đoán muối tiếp theo không có tác động an ninh thực sự; miễn là chúng đến từ một nhóm dữ liệu đủ lớn để tạo ra các loại muối khác nhau cho mỗi mật khẩu được mã hóa, bạn vẫn ổn. Hãy nhớ rằng, muối có ở đó để ngăn chặn việc sử dụng bảng cầu vồng nếu băm của bạn rơi vào tay xấu. Họ không bí mật.
Andrew Moore

7
@AndrewMoore hoàn toàn chính xác! Tuy nhiên, muối phải có đủ entropy để được thống kê độc đáo. Không chỉ trong ứng dụng của bạn, mà trong tất cả các ứng dụng. Vì vậy, mt_rand()có một khoảng thời gian đủ cao, nhưng giá trị hạt giống chỉ là 32 bit. Vì vậy, việc sử dụng mt_rand()có hiệu quả giới hạn bạn chỉ có 32 bit entropy. Điều đó nhờ vào Vấn đề sinh nhật có nghĩa là bạn có 50% cơ hội va chạm chỉ với 7k muối được tạo ra (trên toàn cầu). Vì bcryptchấp nhận 128 bit muối, tốt hơn là sử dụng một nguồn có thể cung cấp tất cả 128 bit ;-). (ở mức 128 bit, 50% khả năng va chạm xảy ra ở 2e19 băm) ...
ircmaxell

1
@ircmaxell: Làm căng "nhóm dữ liệu đủ lớn". Tuy nhiên, nguồn của bạn không phải là nguồn entropy RẤT CAO, chỉ đủ cao cho 128 bit. Tuy nhiên, nếu bạn đã sử dụng hết tất cả các nguồn có sẵn của mình (không có OpenSSL, v.v ...) và dự phòng duy nhất của bạn là mt_rand (), nó vẫn tốt hơn so với nguồn thay thế (đó là rand ()).
Andrew Moore

4
@AndrewMoore: hoàn toàn. Không tranh cãi mà. Chỉ có điều đó mt_randuniqid(và do đó lcg_valuerand) không phải là lựa chọn đầu tiên ...
ircmaxell

1
ircmaxell, cảm ơn bạn rất nhiều về thư viện password_compat cho 5.3.xx, chúng tôi chưa cần điều này trước đây nhưng bây giờ chúng tôi làm, trên máy chủ php 5.3.xx và cảm ơn bạn đã khuyên bạn không cố gắng làm logic này chính mình
Lizardx 8/12/2015

47

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

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ó.


7
Thực thi bất cứ điều gì bạn muốn, người dùng sẽ quản lý để sử dụng cùng một mật khẩu cho nhiều thứ. Vì vậy, bạn phải bảo vệ nó nhiều nhất có thể hoặc thực hiện một cái gì đó cho phép bạn không phải lưu trữ bất kỳ mật khẩu nào (SSO, openID, v.v.).
Arkh

41
Không. Băm mật khẩu được sử dụng để bảo vệ chống lại một cuộc tấn công: ai đó đã đánh cắp cơ sở dữ liệu của bạn và muốn lấy mật khẩu đăng nhập + mật khẩu.
Arkh

4
@Josh K. Tôi khuyến khích bạn cố gắng bẻ khóa một số mật khẩu đơn giản sau khi đưa chúng qua phpass được điều chỉnh để phải mất từ ​​1ms đến 10ms để tính toán nó trên máy chủ web của bạn.
Arkh

3
Đã đồng ý. Nhưng loại người dùng sẽ sử dụng qwerty làm mật khẩu cũng là loại người dùng sẽ đánh dấu bất kỳ ai phức tạp ở đâu đó mà anh ta (và kẻ tấn công) có thể dễ dàng đọc được. Điều sử dụng bcrypt hoàn thành là khi db của bạn công khai trái với ý muốn của bạn, sẽ khó đến với những người dùng có một số mật khẩu như ^ | $$ & ZL6- £ so với khi bạn sử dụng sha512 trong một lần.
Arkh

4
@coreyward đáng lưu ý rằng làm điều đó có hại hơn là không chặn; đó dễ dàng được coi là một vector "từ chối dịch vụ". Chỉ cần bắt đầu spam các thông tin đăng nhập xấu trên bất kỳ tài khoản đã biết nào và bạn có thể phá vỡ nhiều người dùng rất, rất dễ dàng. Thà rằng tarpit (trì hoãn) kẻ tấn công còn hơn là từ chối truy cập hoàn toàn, đặc biệt nếu đó là một khách hàng trả tiền.
damianb

36

Bạn có thể tạo hàm băm một chiều bằng bcrypt bằng crypt()hàm 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


4
Đây là cách tiếp cận đúng - sử dụng crypt()chức năng của PHP , hỗ trợ một số hàm băm mật khẩu khác nhau. Hãy chắc chắn rằng bạn không sử dụng CRYPT_STD_DEShoặc CRYPT_EXT_DES- bất kỳ loại nào được hỗ trợ khác đều ổn (và bao gồm bcrypt, dưới tên CRYPT_BLOWFISH).
phê

4
SHA thực sự cũng có một tham số chi phí, thông qua tùy chọn 'vòng'. Khi sử dụng điều đó, tôi cũng thấy không có lý do gì để ủng hộ bcrypt.
Pieter Ennes

3
Trên thực tế, một mật khẩu SHA-1 (hoặc MD5) duy nhất vẫn dễ dàng có khả năng vũ phu, có hoặc không có muối (muối giúp chống lại các bảng cầu vồng, không chống lại vũ phu). Sử dụng bcrypt.
Paŭlo Ebermann

Tôi thấy phiền khi mọi người dường như nói "bcrypt" khi họ có nghĩa là mật mã của php ().
Sliq

3
@Panique Tại sao? Thuật toán được gọi là bcrypt . cryptlộ ra một số băm mật khẩu, với bcrypt tương ứng với CRYPT_BLOWFISHhằng số. Bcrypt hiện là thuật toán mạnh nhất được hỗ trợ bởi cryptmột số thuật toán khác mà nó hỗ trợ khá yếu.
CodeInChaos

34

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


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.


3
Việc tạo ra muối có thể được cải thiện (sử dụng nguồn ngẫu nhiên của HĐH), nếu không thì nó có vẻ tốt với tôi. Đối với các phiên bản PHP mới hơn, tốt hơn là sử dụng 2ythay vì 2a.
martinstoeckli

sử dụng mcrypt_create_iv($size, MCRYPT_DEV_URANDOM)làm nguồn cho muối.
CodeInChaos

Tôi sẽ xem xét kỹ hơn về mcrypt_create_iv () khi tôi có một chút thời gian, nếu không có gì khác thì nó sẽ cải thiện hiệu suất một chút.
Jon Hulka

2
Thêm mã hóa Base64 và dịch sang bảng chữ cái tùy chỉnh bcryptsử dụng. mcrypt_create_iv(17, MCRYPT_DEV_URANDOM), str_replace('+', '.', base64_encode($rawSalt)) ,$salt = substr($salt, 0, 22);
CodesInChaos

1
@JonHulka - Hãy xem gói tương thích của PHP [Dòng 127], đây là một triển khai đơn giản.
martinstoeckli

29

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ư thế nà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 "cost"tham số, tốt nhất là bỏ qua "salt"tham số, vì hàm đã làm hết sức để tạo ra một loại muối an toàn về mặt mật mã.

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


3
Sau khi tôi đọc nó, suy nghĩ đầu tiên của tôi là "làm thế nào để bạn lưu trữ muối được tạo ra"? Sau khi chọc qua các tài liệu, hàm password_hash () sẽ tạo ra một chuỗi lưu trữ phương thức mã hóa, muối và hàm băm được tạo. Vì vậy, nó chỉ lưu trữ mọi thứ nó cần trong một chuỗi để hàm password_verify () hoạt động. Chỉ muốn đề cập đến điều này vì nó có thể giúp đỡ người khác khi họ xem nó.
jzimmerman2011

@ jzimmerman2011 - Chính xác, trong một câu trả lời khác, tôi đã cố gắng giải thích định dạng lưu trữ này bằng một ví dụ.
martinstoeckli

7

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 ngăn chặn bảng cầu vồng các cuộc 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.


Tôi nghĩ rằng họ cho rằng kẻ tấn công đã tìm cách đánh cắp DB của tôi thông qua một số phương tiện khác và hiện đang cố gắng lấy mật khẩu ra để thử chúng trên paypal hoặc thứ gì đó.
Vilx-

4
Nửa năm 2012 và câu trả lời này vẫn còn rất khó khăn, làm thế nào một thuật toán băm chậm ngăn chặn các cuộc tấn công bảng cầu vồng? Tôi nghĩ rằng một phạm vi byte ngẫu nhiên muối đã làm gì? Tôi luôn nghĩ tốc độ của thuật toán băm chỉ ra số lần lặp mà chúng có thể gửi so với hàm băm mà chúng nhận được từ bạn trong một khoảng thời gian cụ thể. Ngoài ra KHÔNG BAO GIỜ BẮT ĐẦU NGƯỜI DÙNG TRÊN ATTEMPTS ĐĂNG NHẬP tin tưởng tôi, người dùng của bạn sẽ chán ngấy, thường thì trên một số trang web tôi cần đăng nhập gần 5 lần đôi khi trước khi tôi nhớ mật khẩu của mình. Ngoài ra tầng thứ hai không hoạt động, mặc dù hai bước xác thực với mã điện thoại di động có thể.
Sammaye

1
@Sammaye Tôi đồng ý với điều này. Tôi đã thiết lập một khối trên 5 lần đăng nhập thất bại, trước khi tăng nhanh lên 7, sau đó 10 lần vào ngày 20. Không người dùng bình thường nào có 20 lần đăng nhập thất bại nhưng đủ thấp để dễ dàng ngăn chặn các cuộc tấn công vũ phu
Bruce Aldridge

@BruceAldridge Cá nhân tôi nghĩ sẽ tốt hơn nếu làm cho tập lệnh của bạn tạm dừng trong một thời gian ngẫu nhiên sau khi nói, 7 lần đăng nhập thất bại và hiển thị một hình ảnh xác thực thay vì chặn. Chặn là một động thái rất tích cực để thực hiện.
Sammaye

1
@Sammaye Tôi đồng ý khối vĩnh viễn là xấu. Tôi đang đề cập đến một khối tạm thời tăng theo số lần thử thất bại.
Bruce Aldridge

7

Đây là một câu trả lời cập nhật cho câu hỏi cũ này!

Cách đúng để băm mật khẩu trong PHP kể từ 5.5 là password_hash()và cách đúng để xác minh chúng là đúng password_verify()và điều này vẫn đúng trong PHP 8.0. Các hàm này sử dụng băm bcrypt theo mặc định, nhưng các thuật toán mạnh hơn khác đã được thêm vào. Bạn có thể thay đổi hệ số công việc (hiệu quả của mã hóa "mạnh" như thế nào) thông quapassword_hash tham số.

Tuy nhiên, trong khi nó vẫn đủ mạnh, bcrypt không còn được coi là hiện đại ; một bộ thuật toán băm mật khẩu tốt hơn đã xuất hiện có tên là Argon2 , với các biến thể Argon2i, Argon2d và Argon2id. Sự khác biệt giữa chúng (như được mô tả ở đây ):

Argon2 có một biến thể chính: Argon2id và hai biến thể bổ sung: Argon2d và Argon2i. Argon2d sử dụng truy cập bộ nhớ phụ thuộc dữ liệu, điều này làm cho nó phù hợp với các loại tiền điện tử và các ứng dụng chứng minh công việc không có mối đe dọa từ các cuộc tấn công thời gian kênh bên. Argon2i sử dụng truy cập bộ nhớ độc lập dữ liệu, được ưu tiên cho băm mật khẩu và dẫn xuất khóa dựa trên mật khẩu. Argon2id hoạt động như Argon2i trong nửa đầu của lần lặp đầu tiên trên bộ nhớ và là Argon2d cho phần còn lại, do đó cung cấp cả bảo vệ tấn công kênh bên và tiết kiệm chi phí vũ lực do đánh đổi bộ nhớ thời gian.

Hỗ trợ Argon2i đã được thêm vào trong PHP 7.2 và bạn yêu cầu nó như thế này:

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

và hỗ trợ Argon2id đã được thêm vào trong PHP 7.3:

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

Không có thay đổi nào được yêu cầu để xác minh mật khẩu vì chuỗi băm kết quả chứa thông tin về thuật toán, muối và các yếu tố công việc nào được sử dụng khi nó được tạo.

Hoàn toàn riêng biệt (và có phần dư thừa), libsodium (được thêm vào trong PHP 7.2) cũng cung cấp hàm băm Argon2 thông qua các hàm sodium_crypto_pwhash_str ()sodium_crypto_pwhash_str_verify()hoạt động tương tự như các hàm dựng sẵn của PHP. Một lý do có thể cho việc sử dụng những điều này là PHP đôi khi có thể được biên dịch mà không có libargon2, điều này làm cho các thuật toán Argon2 không có sẵn cho hàm password_hash; PHP 7.2 trở lên phải luôn luôn bật libsodium, nhưng có thể không - nhưng ít nhất có hai cách bạn có thể nhận được ở thuật toán đó. Dưới đây là cách bạn có thể tạo hàm băm Argon2id bằng libsodium (ngay cả trong PHP 7.2, nếu không thì không có hỗ trợ Argon2id)):

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

Lưu ý rằng nó không cho phép bạn chỉ định một cách thủ công; đây là một phần trong đặc điểm của libsodium - không cho phép người dùng đặt tham số thành các giá trị có thể ảnh hưởng đến bảo mật - ví dụ: không có gì ngăn bạn chuyển một chuỗi muối rỗng sang password_hashchức năng của PHP ; libsodium không cho phép bạn làm bất cứ điều gì ngớ ngẩn!


4

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

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

1

Như chúng ta đều biết việc lưu trữ mật khẩu trong văn bản rõ ràng trong cơ sở dữ liệu là không an toàn. bcrypt là một kỹ thuật mật khẩu băm. Nó được sử dụng để xây dựng bảo mật mật khẩu. Một trong những chức năng tuyệt vời của bcrypt là nó cứu chúng ta khỏi các tin tặc, nó được sử dụng để bảo vệ mật khẩu khỏi các cuộc tấn công hack vì mật khẩu được lưu trữ ở dạng bcrypted.

hàm password_hash () được sử dụng để tạo hàm băm mật khẩu mới. Nó sử dụng thuật toán băm mạnh và mạnh. Hàm password_hash () tương thích rất nhiều với hàm crypt (). Do đó, băm mật khẩu được tạo bởi crypt () có thể được sử dụng với password_hash () và ngược lại. Các hàm password_verify () và password_hash () chỉ là các hàm bao quanh hàm crypt () và chúng làm cho việc sử dụng nó chính xác dễ dàng hơn nhiều.

Đồng bộ

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

Các thuật toán sau hiện được hỗ trợ bởi hàm password_hash ():

PASSWORD_DEFAULT PASSWORD_BCRYPT PASSWORD_ARGON2I PASSWORD_ARGON2ID

Tham số: Hàm này chấp nhận ba tham số như đã đề cập ở trên và được mô tả bên dưới:

mật khẩu : Nó lưu mật khẩu của người dùng. algo : Đây là hằng số thuật toán mật khẩu được sử dụng liên tục trong khi biểu thị thuật toán sẽ được sử dụng khi quá trình băm mật khẩu diễn ra. tùy chọn : Nó là một mảng kết hợp, chứa các tùy chọn. Nếu điều này được loại bỏ và không bao gồm, một loại muối ngẫu nhiên sẽ được sử dụng và việc sử dụng chi phí mặc định sẽ xảy ra. Trả về giá trị : Nó trả về mật khẩu băm khi thành công hoặc Sai khi thất bại.

Ví dụ :

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

Các chương trình dưới đây minh họa hàm password_hash () trong PHP:

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

ĐẦU RA

$2y$10$Z166W1fBdsLcXPVQVfPw/uRq1ueWMA6sLt9bmdUFz9AmOGLdM393G

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.