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


Mỗi bây giờ và sau đó tôi nghe lời khuyên "Sử dụng bcrypt để lưu trữ mật khẩu trong PHP, bcrypt quy tắc".

Nhưng cái gì là bcrypt? PHP không cung cấp bất kỳ chức năng nào như vậy, việc bập bẹ trên Wikipedia về một tiện ích mã hóa tập tin và các tìm kiếm trên Web chỉ tiết lộ một vài triển khai 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ã có 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 có hàm băm một chiều.

Giải thích là gì?


1148
2018-01-25 15:34


gốc


Câu hỏi này đã được đã giải quyết trước đóvà đề xuất của họ về việc sử dụng thư viện chuẩn là tuyệt vời. An ninh 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 được cái quái gì họ đang làm, bạn chỉ giúp mình. - eykanal
@eykanal - trang đó thậm chí không đề cập đến bcrypt, ít giải thích hơn nhiều nó là gì. - Vilx-
@eykanal - Tôi không yêu cầu giải thích về cách hoạt động của nó. tôi chỉ muốn biết gì nó là. Bởi vì bất cứ điều gì tôi có thể đào lên trên mạng theo từ khóa "bcrypt", có thể không được sử dụng cho mật khẩu băm. Không trực tiếp, và không phải trong PHP. OK, bây giờ tôi hiểu rằng nó thực sự là "phpass" gói sử dụng blowfish để mã hóa mật khẩu của bạn với một khóa có nguồn gốc từ mật khẩu của bạn (trong bản chất mã hóa mật khẩu với chính nó). Nhưng tham khảo nó như là "bcrypt" là gây hiểu lầm nghiêm trọng, và đó là những gì tôi muốn làm rõ trong câu hỏi này. - Vilx-
@Vilx: Tôi đã thêm thông tin thêm về lý do bcrypt là một thuật toán băm một chiều so với lược đồ mã hóa trong câu trả lời của tôi. Có toàn bộ quan niệm sai lầm này bcrypt chỉ là Blowfish khi trên thực tế nó có một lịch trình 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ã hóa mà không biết trạng thái ban đầu của mật mã (muối, vòng, khóa). - Andrew Moore
Xem thêm Openwall's Khung mật khẩu băm mật khẩu PHP di động (PHPass). Nó cứng rắn chống lại một số cuộc tấn công phổ biến trên mật khẩu người dùng. - jww


Các câu trả lời:


bcrypt là một thuật toán băm có khả năng mở rộng với phần cứng (thông qua một số vòng cấu hình). Sự chậm chạp của nó và nhiều vòng đảm bảo rằng kẻ tấn công phải triển khai các quỹ lớn và phần cứng để có thể crack 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 mà không có một số tiền lố bịch hoặc phần cứng.

bcrypt sử dụng Eksblowfish thuật toán để băm mật khẩu. Trong khi giai đoạn mã hóa của Eksblowfish và Blowfish giống hệt nhau, giai đoạn lịch chính của Eksblowfish đảm bảo rằng bất kỳ trạng thái tiếp theo nào 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 precomputed mà không có kiến ​​thức của cả hai. Vì sự khác biệt chính này, bcrypt là một thuật toán băm một chiều. Bạn không thể truy xuất mật khẩu thuần văn bản mà không biết muối, viên đạn và chìa khóa (mật khẩu). [Nguồn]

Cách sử dụng bcrypt:

Sử dụng PHP> = 5.5-DEV

Hàm băm mật khẩu hiện đã được xây dựng trực tiếp vào PHP> = 5.5. Bây giờ bạn có thể sử dụng password_hash() để tạo ra một bcrypt 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 dựa vào 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)

Đây là 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 ban đầu được viết bằng C, cung cấp chức năng tương tự. Khi thư viện tương thích được cài đặt, việc sử dụng giống như trên (trừ ký pháp mảng viết tắt nếu bạn vẫn ở trên nhánh 5.3.x).

Sử dụng PHP <5.3.7 (KHÔNG ĐƯỢC DÙNG)

Bạn có thể dùng crypt() để tạo băm mật mã của chuỗi đầu vào. Lớp này có thể tự động tạo ra các muối và kiểm tra các băm hiện có đối với một đầ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. Lựa chọn thay thế này chỉ được cung cấp cho các 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ư sau:

$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 Hashing Framework.


975
2018-06-12 19:23



@ The Wicked Flea: Xin lỗi vì đã làm bạn thất vọng, nhưng mt_rand() cũng được gieo hạt bằng cách sử dụng thời gian hiện tại và ID tiến trình hiện tại. Xin vui lòng xem GENERATE_SEED() trong /ext/standard/php_rand.h. - Andrew Moore
@ Mike: Đi trước, nó có cho chính xác lý do đó! - Andrew Moore
Đối với bất cứ ai nghĩ rằng họ cần phải sửa đổi sự bắt đầ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ừ tài liệu: "Blowfish băm với một muối như sau:" $ 2a $ ", thông số chi phí hai chữ số," $ "và 22 chữ số từ bảng chữ cái". - jwinn
@ MichaelLang: Điều tốt crypt() được xem xét ngang hàng và xác minh sau đó. Đoạn mã trên gọi PHP crypt(), gọi POSIX crypt() chức năng. Tất cả các mã trên làm nhiều hơn là tạo ra một muối ngẫu nhiên (mà không cần phải được mã hóa an toàn, muối không được coi là một bí mật) trước khi gọi crypt(). Có lẽ bạn nên tự mình nghiên cứu một chút trước khi gọi sói. - Andrew Moore
Xin lưu ý rằng câu trả lời này, trong khi tốt, đang bắt đầu hiển thị 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()) là đối tượng bị 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 của vấn đề liên quan đây. Cũng xin lưu ý rằng mới API băm mật khẩu (lib tương thích ngược) bây giờ là phương pháp ưa thích của việc triển khai băm mật khẩu bcrypt trong ứng dụng của bạn. - DaveRandom


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 mã hóa khác, bạn không nên tự mình làm điều đó. Nếu bạn cần phải lo lắng về bất cứ điều gì như quản lý các phím, hoặc lưu trữ muối hoặc tạo ra các số ngẫu nhiên, bạn đang làm sai.

Lý do rất đơn giản: nó rất dễ dàng vít lên bcrypt. Trong thực tế, nếu bạn nhìn vào hầu hết các đ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ó khăn.

Hãy để nó cho các chuyên gia. Hãy để nó cho những người làm việc để duy trì những 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 về 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 cho mật khẩu băm đang được giới thiệu. Ngoài ra còn có một thư viện tương thích tạm thời được duy trì (bởi tôi) cho 5.3.7+. Điều này có lợi ích khi được xem xét 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ư API 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:

PasswordLib

Đây là một cách tiếp cận hơi khác với băm mật khẩu. Thay vì chỉ 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 kế thừa 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
    }
}

Tham khảo:

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

PHPASS

Đây là một lớp hỗ trợ bcrypt, nhưng cũng hỗ trợ một 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

Chú thích: 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 các thư viện này trả về một chuỗi. Đó là vì BCrypt hoạt động như thế nào trong nội bộ. Và có một TON câu trả lời về điều đó. Dưới đây là một lựa chọn mà tôi đã viết, rằng tôi sẽ không sao chép / dán ở đây, nhưng liên kết đến:

Gói lại

Có rất 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ẽ CAO 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ó thể bạn đang làm điều gì đó sai. Nếu mã của bạn đang sử dụng hash() (hoặc là md5() hoặc là sha1()) trực tiếp, bạn gần như chắc chắn làm điều gì đó sai trái.

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


273
2018-01-25 15:46



Muối phải được tạo ngẫu nhiên, tuy nhiên nó không cần phải đến từ một nguồn ngẫu nhiên an toàn. Muối không phải là bí mật. Có thể đoán được 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 muối khác nhau cho mỗi mật mã được mã hóa, bạn vẫn ổn. Hãy nhớ rằng, muối là có để ngăn chặn việc sử dụng các bảng cầu vồng nếu băm của bạn đi vào tay xấu. Họ không phải là bí mật. - Andrew Moore
@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, nhưng trong tất cả các ứng dụng. Vì thế mt_rand() có thời gian đủ cao, nhưng giá trị hạt giống chỉ là 32 bit. Vì vậy, sử dụng mt_rand() giới hạn hiệu quả bạn chỉ với 32 bit entropy. Mà 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 tạo ra (trên toàn cầu). Vì bcrypt chấp nhận 128 bit muối, tốt hơn là sử dụng nguồn có thể cung cấp tất cả 128 bit ;-). (tại 128 bit, 50% cơ hội va chạm xảy ra ở 2e19 băm) ... - ircmaxell
@ircmaxell: Hense "đủ lớn hồ bơi của dữ liệu". 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 đã cạn kiệt tất cả các nguồn có sẵn của bạn (không có OpenSSL, vv ...) và dự phòng duy nhất của bạn là mt_rand (), nó vẫn tốt hơn so với thay thế (đó là rand ()). - Andrew Moore
@AndrewMoore: tuyệt đối. Không tranh luận về điều đó. Chỉ vậy thôi mt_rand và uniqid(và do đó lcg_value và rand) không phải là lựa chọn đầu tiên ... - ircmaxell
ircmaxell, cảm ơn bạn rất nhiều vì thư viện password_compat cho 5.3.xx, chúng tôi không 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 lời khuyên rõ ràng của bạn để không cố gắng thực hiện logic này chính mình. - Lizardx


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

Mục đích 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 cố gắng để bạo lực nó (một sự chậm trễ 10 ms để kiểm tra một mật khẩu là không có gì cho bạn, rất nhiều cho một người nào đó cố gắng để bạo lực nó). Bcrypt là chậm và có thể được sử dụng với tham số để chọn tốc độ chậm.


43
2018-01-25 15:48



Thực thi bất cứ điều gì bạn muốn, người dùng sẽ quản lý để vít lên và sử dụng cùng một mật khẩu trên nhiều thứ. Vì vậy, bạn phải bảo vệ nó càng nhiều càng tốt hoặc thực hiện một cái gì đó mà cho phép bạn không phải lưu trữ bất kỳ mật khẩu (SSO, openID vv). - Arkh
Không. Mật khẩu băm được sử dụng để bảo vệ chống lại một cuộc tấn công: ai đó đã lấy cắp cơ sở dữ liệu của bạn và muốn nhận mật khẩu đăng nhập hoặc mật khẩu rõ ràng. - Arkh
@ Josh K. Tôi khuyến khích bạn cố gắng để crack một số mật khẩu đơn giản sau khi nhận được chúng thông qua phpass tuned vì vậy phải mất từ ​​1ms và 10ms để tính toán nó trên máy chủ web của bạn. - Arkh
Đã đồng ý. Nhưng loại người dùng sẽ sử dụng qwerty như một mật khẩu cũng là loại người dùng sẽ đánh dấu bất kỳ một phức tạp ở đâu đó (và kẻ tấn công) có thể dễ dàng đọc nó. Những gì sử dụng bcrypt là khi db của bạn công khai với ý muốn của bạn, sẽ khó khăn hơn nếu người dùng có mật khẩu như ^ | $$ & ZL6- £ hơn nếu bạn sử dụng sha512 trong một lần. - Arkh
@coreyward đáng chú ý rằng làm điều đó có hại hơn là không chặn gì cả; dễ dàng được coi là vectơ "từ chối dịch vụ". Chỉ cần bắt đầu gửi spam đăng nhập xấu trên bất kỳ tài khoản đã biết nào và bạn có thể làm gián đoạn nhiều người dùng rất, rất dễ dàng. Nó tốt hơn để tarpit (trì hoãn) kẻ tấn công hơn là từ chối hoàn toàn truy cập, đặc biệt nếu đó là một khách hàng trả tiền. - damianb


Bạn có thể tạo một băm một chiều với bcrypt bằng cách sử dụng PHP's crypt() chức năng và đi qua trong một muối Blowfish thích hợp. Điều quan trọng nhất trong toàn bộ phương trình là A) thuật toán chưa bị xâm phạm và B) bạn đúng muối mỗi mật khẩu. Không sử dụng muối 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 - Crypt Chức năng


34
2017-10-31 08:25



Đây là cách tiếp cận đúng - sử dụng PHP crypt() , hỗ trợ nhiều hàm băm mật khẩu khác nhau. Đảm bảo bạn không sử dụng CRYPT_STD_DES hoặc là CRYPT_EXT_DES - bất kỳ loại được hỗ trợ nào khác đều tốt (và bao gồm bcrypt, dưới tên CRYPT_BLOWFISH). - caf
SHA thực sự có một tham số chi phí là tốt, thông qua tùy chọn 'vòng'. Khi sử dụng nó, tôi cũng không thấy lý do gì để ủng hộ bcrypt. - Pieter Ennes
Trên thực tế, một SHA-1 (hoặc MD5) duy nhất của mật khẩu vẫn dễ dàng brute-lực-có thể, 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 brute-buộc). Sử dụng bcrypt. - Paŭlo Ebermann
Tôi thấy nó làm phiền rằng mọi người dường như nói "bcrypt" khi họ ngụ ý crypt của php (). - Sliq
@Panique Tại sao? Thuật toán được gọi là bcrypt. crypt cho thấy nhiều băm mật khẩu, với bcrypt tương ứng với CRYPT_BLOWFISH không thay đổi. Bcrypt hiện là thuật toán mạnh nhất được hỗ trợ bởi crypt và một số khác nó hỗ trợ khá yếu. - CodesInChaos



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 thế.


Mọi người đều muốn làm đ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 nó phải rõ ràng, nhưng xin vui lòng không sử dụng 'mật khẩu' làm mật khẩu của bạn.


32
2018-01-11 08:07



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ệ điều hành), nếu không 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 nên sử dụng 2y thay vì 2a. - martinstoeckli
sử dụng mcrypt_create_iv($size, MCRYPT_DEV_URANDOM) như là nguồn cho muối. - CodesInChaos
Tôi sẽ xem xét kỹ hơn về mcrypt_create_iv () khi tôi nhận được một chút thời gian, nếu không có gì khác, nó sẽ cải thiện hiệu suất một chút. - Jon Hulka
Thêm mã hóa Base64 và dịch sang bảng chữ cái tùy chỉnh bcrypt sử dụng. mcrypt_create_iv(17, MCRYPT_DEV_URANDOM), str_replace('+', '.', base64_encode($rawSalt)), $salt = substr($salt, 0, 22); - CodesInChaos
@ JonHulka - Hãy xem PHP gói tương thích[Dòng 127], đây là một triển khai đơn giản. - martinstoeckli


Phiên bản 5.5 của PHP sẽ được hỗ trợ sẵn cho BCrypt, các chức năng password_hash() và password_verify(). Trên thực tế đây chỉ là hàm bao quanh hàm crypt()và sẽ dễ sử dụng hơn. Nó đảm nhiệm việc tạo ra một 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 với 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 thông 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 thông số chi phí, bạn có thể làm như thế này, tăng thông 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" thông số, tốt nhất là bỏ qua "salt" tham số, bởi vì hàm đã làm hết sức mình để tạo ra một muối an toàn mã hóa.

Đối với phiên bản PHP 5.3.7 trở lên, có tồn tại gói tương thích, từ cùng một tác giả đã tạo 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ợ 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, đó là giải pháp thay thế tốt nhất cho các phiên bản PHP trước đó.


24
2018-02-19 14:17



Sau khi tôi đọc điều này, 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 xem qua các tài liệu, hàm password_hash () kết thúc bằng việc 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 ra. Vì vậy, nó chỉ lưu trữ mọi thứ cần thiết trong một chuỗi cho 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 điều này. - jzimmerman2011
@ jzimmerman2011 - Chính xác, trong một câu trả lời tôi đã cố gắng giải thích định dạng lưu trữ này với một ví dụ. - martinstoeckli


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


5
2017-12-07 20:56





Tư duy hiện tại: băm nên có sẵn chậm nhất, không phải là nhanh nhất có thể. Điều này ngăn chặn cầu vồng bảng tấn công.

Cũng liên quan, nhưng phòng ngừa: Một kẻ tấn công không bao giờ nên 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 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 bằng lời giải thích. Cách tiếp cận thứ hai là có một lược đồ mật khẩu hai tầng, giống như các ngân hàng. Đưa ra một khóa cho thất bại trên vượt qua thứ hai tăng cường an ninh.

Tóm tắt: làm chậm kẻ tấn công bằng cách sử dụng hàm băm tốn thời gian. Đồng thời, 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 tầng mật khẩu thứ hai.


4
2018-03-25 16:55



Tôi nghĩ rằng họ cho rằng kẻ tấn công đã quản lý để ăn cắp DB của tôi thông qua một số phương tiện khác, và bây giờ đang cố gắng để có được các mật khẩu ra để thử chúng trên paypal hoặc một cái gì đó. - Vilx-
Một nửa trong suốt năm 2012 và câu trả lời này vẫn rất khả quan, 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 của bảng cầu vồng? Tôi nghĩ rằng một dải muối ngẫu nhiên đã làm gì? Tôi luôn nghĩ rằng tốc độ của thuật toán băm chỉ ra có bao nhiêu lần lặp lại mà chúng có thể gửi đối với băm mà chúng tạo thành cho bạn trong một khoảng thời gian cụ thể. Ngoài ra KHÔNG BAO GIỜ BAO GIỜ CHẶN NGƯỜI DÙNG ĐĂNG NHẬP ĐĂNG NHẬP TIN TỨC tin tưởng tôi, người dùng của bạn sẽ bị chán, thường trên một số trang web tôi cần phải đăng nhập gần 5 lần nhiều hơn trước khi tôi nhớ mật khẩu của mình. Ngoài ra tầng thứ hai vượt qua không hoạt động, hai bước auth với mã điện thoại di động có thể mặc dù. - Sammaye
@Sammaye Tôi sẽ đồng ý với điều này đến một điểm. Tôi thiết lập một khối trên 5 lần đăng nhập không thành công, trước khi tăng nhanh lên 7, sau đó 10 giờ ngồi trên 20. Không có 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 bạo lực - Bruce Aldridge
@BruceAldridge Cá nhân tôi sẽ nghĩ rằng sẽ tốt hơn nếu tạm dừng kịch bản của bạn trong một thời gian ngẫu nhiên sau khi nói, 7 lần đăng nhập không thành công và hiển thị hình ảnh xác thực thay vì chặn. Chặn là một động thái rất lớn để thực hiện. - Sammaye
@Sammaye Tôi đồng ý các 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 với số lần thử không thành công. - Bruce Aldridge


Dành cho OAuth 2 mật khẩu:

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

2