프로그래밍 언어/PHP

PHP에서 암호를 해싱하기 위해 bcrypt를 사용하는 방법

Rateye 2021. 7. 5. 10:31
728x90
반응형

 

질문 : PHP에서 암호를 해싱하기 위해 bcrypt를 어떻게 사용합니까?

때때로 나는 "PHP, bcrypt 규칙에 암호를 저장하기 위해 bcrypt 사용"이라는 조언을 듣습니다.

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

설명은 무엇입니까?

답변

bcrypt 는 (구성 가능한 라운드 수를 통해) 하드웨어로 확장 가능한 해싱 알고리즘입니다. 속도가 느리고 여러 차례 진행되므로 공격자가 암호를 해독 할 수 있도록 막대한 자금과 하드웨어를 배치해야합니다. 암호 단위 솔트 ( bcrypt 솔트)에 추가하면 터무니없는 자금이나 하드웨어 없이는 공격이 사실상 불가능하다는 것을 확신 할 수 있습니다.

bcrypt 는 Eksblowfish 알고리즘을 사용하여 암호를 해시합니다. Eksblowfish와 복어의 암호화 단계는 동일하지만, 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.';
          }
          

GitHub 에는 원래 C로 작성된 위 함수의 소스 코드를 기반으로 생성 된 호환성 라이브러리 가 있으며 동일한 기능을 제공합니다. 호환성 라이브러리가 설치되면 사용법은 위와 동일합니다 (아직 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를 사용할 수도 있습니다.

출처 : https://stackoverflow.com/questions/4795385/how-do-you-use-bcrypt-for-hashing-passwords-in-php
728x90
반응형