복붙노트

어떻게 bcrypt를 사용하여 PHP에서 암호를 해싱합니까?

PHP

어떻게 bcrypt를 사용하여 PHP에서 암호를 해싱합니까?

때때로 나는 "PHP, bcrypt 규칙에 암호 저장에 bcrypt 사용"이라는 조언을 듣는다.

하지만 bcrypt는 무엇입니까? PHP는 그러한 기능을 제공하지 않으며, 파일 암호화 유틸리티에 대한 위키피디아 (Wikipedia)의 말다툼과 웹 검색은 다른 언어로 된 Blowfish의 구현을 보여줍니다. 이제 Blowfish는 mcrypt를 통해 PHP에서도 사용할 수 있지만 암호 저장과 관련하여 어떻게 도움이됩니까? 복어는 범용 암호이며 두 가지 방식으로 작동합니다. 암호화가 가능하면 해독 할 수 있습니다. 암호에는 단방향 해시 기능이 필요합니다.

설명은 무엇입니까?

해결법

  1. ==============================

    1.

    bcrypt는 하드웨어로 확장 가능한 해싱 알고리즘입니다 (구성 가능한 라운드 수를 통해). 속도가 느리고 여러 라운드를 거치면 공격자가 암호를 해독 할 수있는 막대한 자금과 하드웨어를 배포해야합니다. 패스워드 당 소금 (bcrypt REQUIRES salts)을 추가하면 우스꽝스러운 자금이나 하드웨어없이 공격이 사실상 불가능하다는 것을 확신 할 수 있습니다.

    bcrypt는 Eksblowfish 알고리즘을 사용하여 암호를 해시합니다. Eksblowfish와 Blowfish의 암호화 단계는 정확히 동일하지만, Eksblowfish의 주요 일정 단계에서는 모든 후속 상태가 소금과 키 (사용자 암호)에 따라 달라지며 둘 다 알고 있지 않은 상태는 사전 계산할 수 없습니다. 이 주요 차이점 때문에 bcrypt는 단방향 해싱 알고리즘입니다. 소금, 라운드 및 키 (암호)를 알지 못하면 일반 텍스트 암호를 검색 할 수 없습니다. [출처]

    암호 해시 함수는 이제 PHP> = 5.5에 직접 작성되었습니다. 이제 password_hash ()를 사용하여 암호의 bcrypt 해시를 만들 수 있습니다.

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

    기존 해시에 대해 사용자가 제공 한 비밀번호를 확인하려면 password_verify ()를 다음과 같이 사용할 수 있습니다.

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

    동일한 기능을 제공하는 원래 C로 작성된 위 함수의 소스 코드를 기반으로 만들어진 GitHub에는 호환성 라이브러리가 있습니다. 일단 호환성 라이브러리가 설치되면 사용법은 위와 동일합니다 (여전히 5.3.x 브랜치에있는 경우 약식 배열 표기법을 빼십시오).

    crypt () 함수를 사용하여 입력 문자열의 bcrypt 해시를 생성 할 수 있습니다. 이 클래스는 자동으로 소금을 생성하고 입력에 대해 기존 해시를 확인합니다. 5.3.7보다 크거나 같은 PHP 버전을 사용하고 있다면, 내장 함수 나 compat 라이브러리를 사용하는 것이 좋습니다. 이 대안은 역사적인 목적으로 만 제공됩니다.

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

    이 코드는 다음과 같이 사용할 수 있습니다.

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

    또는 Portable PHP Hashing Framework를 사용할 수도 있습니다.

  2. ==============================

    2.

    그래서, 당신은 bcrypt를 사용하고 싶습니까? 굉장해! 그러나 다른 암호 작성 영역과 마찬가지로 직접 작성하지 않아야합니다. 키를 관리하거나 소금을 저장하거나 임의의 숫자를 생성하는 것과 같은 것에 대해 걱정할 필요가 있다면 잘못하고있는 것입니다.

    그 이유는 간단합니다 : bcrypt를 망치는 것은 아주 쉽습니다. 사실,이 페이지에서 거의 모든 코드를 보면 이러한 공통적 인 문제 중 적어도 하나를 위반하고 있음을 알 수 있습니다.

    전문가를 위해 그것을 남겨주세요. 이 도서관을 유지하는 일을하는 사람들을 위해 남겨주세요. 결정을 내릴 필요가 있다면 잘못하고있는 것입니다.

    대신 라이브러리를 사용하십시오. 요구 사항에 따라 몇 가지가 존재합니다.

    다음은 좀 더 일반적인 API의 일부입니다.

    PHP 5.5부터 암호 해싱을위한 새로운 API가 소개되었습니다. 또한 5.3.7+ 용으로 유지되는 shim 호환 라이브러리가 있습니다. 이는 동료 검토를 거쳐 사용하기 쉬운 구현의 이점이 있습니다.

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

    정말로, 그것은 매우 단순 해지기위한 것입니다.

    자원:

    이것은 PHP 5.5와 비슷한 또 다른 API이며 비슷한 목적을 수행합니다.

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

    자원:

    이는 암호 해싱과 약간 다른 접근 방식입니다. PasswordLib는 bcrypt를 지원하는 것보다 많은 수의 해싱 알고리즘을 지원합니다. 주로 통제 범위를 벗어난 레거시 시스템 및 다른 시스템과의 호환성을 지원해야하는 상황에서 유용합니다. 그것은 많은 해싱 알고리즘을 지원합니다. 지원되는 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
        }
    }
    

    참고 문헌 :

    이 레이어는 bcrypt를 지원하지만, PHP> = 5.3.2에 대한 액세스 권한이없는 경우에 유용한 매우 강력한 알고리즘을 지원합니다 ... 실제로 PHP 3.0 이상을 지원합니다 (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
        }
    }
    

    자원

    참고 : openwall에서 호스팅되지 않은 PHPASS 대안을 사용하지 마십시오. 다른 프로젝트입니다!

    알아 차리면,이 라이브러리들은 모두 하나의 문자열을 반환합니다. 이는 BCrypt가 내부적으로 어떻게 작동하는지 때문입니다. 그리고 거기에 대한 답변이 많습니다. 여기에 필자가 작성한 선택 사항이 있습니다. 여기서 복사 / 붙여 넣기는하지 않지만 링크는 다음과 같습니다.

    다양한 선택이 있습니다. 당신이 선택한 것은 당신에게 달렸습니다. 그러나 위의 라이브러리 중 하나를 사용하여이 작업을 처리하는 것이 좋습니다.

    다시 말하지만 crypt ()를 직접 사용하는 경우 잘못된 결과가 발생할 수 있습니다. 코드에서 hash () (또는 md5 () 또는 sha1 ())를 직접 사용하는 경우 거의 틀린 작업을 수행하고있는 것입니다.

    그냥 도서관을 이용하십시오 ...

  3. ==============================

    3.

    레인보우 테이블만으로도 많은 정보를 얻을 수 있습니다 : 보안 암호 체계 또는 휴대용 PHP 암호 해시 프레임 워크에 대해 알아야 할 사항.

    목표는 속도가 느린 암호를 해시하는 것입니다. 따라서 암호 데이터베이스를 얻는 누군가가 무차별 공격을 시도하여 죽을 것입니다 (암호를 확인하기위한 10 밀리 초 지연은 아무 것도 아니며 무차별 대입하려는 사람에게 많은 일입니다). Bcrypt는 속도가 느리며 매개 변수와 함께 사용하여 느린 속도를 선택할 수 있습니다.

  4. ==============================

    4.

    PHP의 crypt () 함수를 사용하고 적절한 Blowfish salt를 전달하여 bcrypt를 사용하여 단방향 해시를 생성 할 수 있습니다. 전체 방정식의 가장 중요한 점은 A) 알고리즘이 손상되지 않았고 B) 각 암호를 제대로 소금다는 것입니다. 응용 프로그램 전체의 소금을 사용하지 마십시오; 전체 응용 프로그램을 열어 단일 레인보우 테이블 세트에서 공격합니다.

    PHP - 크립트 함수

  5. ==============================

    5.

    수정 : 2013.01.15 - 서버가 지원한다면 martinstoeckli의 솔루션을 대신 사용하십시오.

    모두는 그것이 이것을보다 복잡하게 만들고 싶어합니다. crypt () 함수는 대부분의 작업을 수행합니다.

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

    예:

    $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
    

    나는 그것이 분명해야한다는 것을 알고 있지만, 귀하의 비밀번호로 '비밀번호'를 사용하지 마십시오.

  6. ==============================

    6.

    PHP 버전 5.5에는 BCrypt, password_hash () 및 password_verify () 함수가 내장되어 있습니다. 실제로 이것은 crypt () 함수를 감싸는 래퍼 일 뿐이며 올바르게 사용하는 것이 더 쉽습니다. 안전한 무작위 염의 생성을 처리하고 좋은 기본값을 제공합니다.

    이 기능을 사용하는 가장 쉬운 방법은 다음과 같습니다.

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

    이 코드는 암호를 BCrypt (알고리즘 2y)로 해시하고 OS 무작위 소스에서 임의의 소금을 생성하며 기본 비용 매개 변수 (이 순간은 10)를 사용합니다. 두 번째 행은 사용자가 입력 한 암호가 이미 저장된 해시 값과 일치하는지 확인합니다.

    비용 매개 변수를 변경하려면 다음과 같이 할 수 있습니다. 비용 매개 변수를 1 씩 늘리면 해시 값을 계산하는 데 필요한 시간이 두 배가됩니다.

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

    "비용"매개 변수와 달리 "소금"매개 변수는 생략하는 것이 가장 좋습니다. 왜냐하면이 기능이 이미 암호 상 안전한 소금을 만들기 위해 최선을 다하고 있기 때문입니다.

    PHP 5.3.7 및 이후 버전에는 password_hash () 함수를 작성한 동일한 작성자의 호환성 팩이 있습니다. 5.3.7 이전의 PHP 버전에서는 unicode safe BCrypt 알고리즘 인 2y가있는 crypt ()가 지원되지 않습니다. 대신에 이전 버전의 PHP 대신 사용할 수있는 2a로 대체 할 수 있습니다.

  7. ==============================

    7.

    다른 대안은 자신의 논문에서 Colin Percival이 bcrypt보다 우월하도록 특별히 고안된 scrypt를 사용하는 것입니다. PECL에는 script PHP 확장이 있습니다. 이상적으로이 알고리즘은 password_ * 함수 (이상적으로는 "PASSWORD_SCRYPT")에 지정할 수 있도록 PHP에 롤백되지만 아직 존재하지 않습니다.

  8. ==============================

    8.

    현재의 생각 : 해시는 가능한 가장 느린 것이 아니라 가능한 가장 느린 것이어야합니다. 이것은 레인보우 테이블 공격을 억제합니다.

    또한 관련 있지만 사전 예방 적 : 침입자는 로그인 화면에 무제한으로 액세스해서는 안됩니다. 이를 방지하려면 : URI와 함께 모든 히트를 기록하는 IP 주소 추적 테이블을 설정하십시오. 5 분 이상에 동일한 IP 주소에서 5 회 이상의 로그인 시도가 발생하면 설명으로 차단하십시오. 두 번째 방법은 은행처럼 2 단계 암호 체계를 사용하는 것입니다. 두 번째 패스에서 실패에 대한 잠금을 설정하면 보안이 향상됩니다.

    요약 : 시간이 많이 걸리는 해시 함수를 사용하여 공격자의 속도를 줄입니다. 또한 로그인에 너무 많은 액세스를 차단하고 두 번째 비밀번호 등급을 추가하십시오.

  9. ==============================

    9.

    OAuth 2 비밀번호 :

    $bcrypt = new \Zend\Crypt\Password\Bcrypt;
    $bcrypt->create("youpasswordhere", 10)
    
  10. from https://stackoverflow.com/questions/4795385/how-do-you-use-bcrypt-for-hashing-passwords-in-php by cc-by-sa and MIT lisence