때때로 나는 “PHP에 암호를 저장하기 위해 bcrypt를 사용하고, bcrypt 규칙”이라는 조언을 듣는다.
그러나 무엇 bcrypt
입니까? PHP는 그러한 기능을 제공하지 않으며, Wikipedia는 파일 암호화 유틸리티에 대해 이야기하고 웹 검색 은 다른 언어로 된 Blowfish 의 몇 가지 구현을 보여줍니다 . 이제 Blowfish는 PHP를 통해 사용할 수도 mcrypt
있지만 암호 저장에 어떤 도움이됩니까? 복어는 범용 암호이며 두 가지 방식으로 작동합니다. 암호화 할 수 있으면 해독 할 수 있습니다. 암호는 단방향 해싱 기능이 필요합니다.
설명은 무엇입니까?
답변
bcrypt
하드웨어로 확장 가능한 해싱 알고리즘입니다 (구성 가능한 라운드 수를 통해). 속도가 느리고 여러 번 진행되므로 공격자는 암호를 해독 할 수 있도록 막대한 자금과 하드웨어를 배포해야합니다. 암호 당 소금 ( 소금이bcrypt
필요)에 추가하면 돈이나 하드웨어없이 어마 어마한 공격을 수행 할 수 없습니다.
bcrypt
Eksblowfish 알고리즘을 사용하여 비밀번호를 해시합니다. 의 암호화 단계 동안 Eksblowfish 와 복어가 정확히 동일의 주요 일정 단계 Eksblowfish는 이후의 상태가 모두 소금과 키 (사용자 암호)에 의존한다는 것을 보장하고, 어떤 상태는 모두의 지식없이 미리 계산 될 수 없다. 이 주요 차이점으로 인해 bcrypt
단방향 해싱 알고리즘이 있습니다. 솔트, 라운드 및 키 (암호)를 모르면 일반 텍스트 비밀번호를 검색 할 수 없습니다 . [ 출처 ]
bcrypt를 사용하는 방법 :
PHP 사용> = 5.5-DEV
패스워드 해싱 함수 는 이제 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.';
}
PHP 사용> = 5.3.7, <5.5-DEV (또한 RedHat PHP> = 5.3.3)
GitHub 에는 원래 C로 작성된 위 함수의 소스 코드를 기반으로 생성 된 호환성 라이브러리 가 있으며 동일한 기능을 제공합니다. 호환성 라이브러리가 설치되면 사용법은 위와 동일합니다 (여전히 5.3.x 분기에있는 경우 속기 배열 표기법 제외).
PHP 사용 <5.3.7 (DEPRECATED)
crypt()
함수를 사용 하여 입력 문자열의 암호화 해시를 생성 할 수 있습니다 . 이 클래스는 솔트를 자동으로 생성하고 입력에 대해 기존 해시를 확인할 수 있습니다. 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를 사용할 수도 있습니다 .
답변
그렇다면 bcrypt를 사용하고 싶습니까? 대박! 그러나 다른 암호화 영역과 마찬가지로 직접 암호화해서는 안됩니다. 키 관리, 소금 저장 또는 임의의 숫자 생성과 같은 것에 대해 걱정이 필요하면 잘못하고 있습니다.
그 이유는 간단합니다. bcrypt를 망가 뜨리는 것은 매우 쉽습니다 . 실제로이 페이지의 거의 모든 코드를 살펴보면 이러한 일반적인 문제 중 하나 이상을 위반하고 있음을 알 수 있습니다.
Face It, 암호화는 어렵다.
전문가에게 맡기십시오. 이 라이브러리를 유지 관리하는 것이 직업인 사람들에게 맡기십시오. 결정을 내려야한다면 잘못하고있는 것입니다.
대신 라이브러리를 사용하십시오. 요구 사항에 따라 몇 가지가 있습니다.
도서관
다음은 더 일반적인 API 중 일부입니다.
PHP 5.5 API-(5.3.7 이상에서 사용 가능)
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.net
- 호환성 라이브러리 : GitHub에서
- PHP의 RFC : wiki.php.net에서
Zend \ Crypt \ Password \ Bcrypt (5.3.2+)
이것은 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
}
}
자원:
- 설명서 : Zend
- 블로그 게시물 : Zend Crypt를 사용한 비밀번호 해싱
PasswordLib
이것은 암호 해싱에 대한 약간 다른 접근 방식입니다. 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
}
}
참고 문헌 :
- 소스 코드 / 문서 : GitHub
PHPASS
이것은 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
}
}
자원
- 코드 : cvsweb
- 프로젝트 사이트 : OpenWall
- 의 <5.3.0 알고리즘의 검토 : StackOverflow의에
참고 : openwall에서 호스팅되지 않는 PHPASS 대안을 사용하지 마십시오 . 다른 프로젝트입니다 !!!
BCrypt 소개
알다시피, 이러한 라이브러리는 모두 단일 문자열을 반환합니다. BCrypt가 내부적으로 작동하는 방식 때문입니다. 그리고 그것에 대한 많은 대답이 있습니다. 여기에 내가 작성하고 복사 / 붙여 넣지 않고 링크하는 선택이 있습니다.
- 해싱 알고리즘과 암호화 알고리즘의 근본적인 차이점 -용어와 이에 대한 기본 정보를 설명합니다.
- 레인보우 테이블이없는 해시 반전 -기본적으로 왜 bcrypt를 사용해야 하는가 …
- bcrypt 해시 저장 -기본적으로 해시 결과에 솔트와 알고리즘이 포함되는 이유는 무엇입니까?
- bcrypt 해시 비용을 업데이트하는 방법 -기본적으로 bcrypt 해시 비용 을 선택하고 유지하는 방법.
- bcrypt로 긴 암호를 해시하는 방법-bcrypt 의 72 자 암호 제한을 설명합니다.
- bcrypt가 소금을 사용하는 방법
- 소금과 후추를 바르기위한 모범 사례 -기본적으로 “고추”를 사용하지 마십시오
- 이전
md5
비밀번호를 bcrypt로 마이그레이션
마무리
많은 다른 선택이 있습니다. 당신이 선택한 것은 당신에게 달려 있습니다. 그러나, 나는 것이다 높게 당신이 당신을 위해 이것을 처리하기위한 위의 라이브러리 중 하나를 사용하는 것이 좋습니다.
다시 말하지만, crypt()
직접 사용 하는 경우 무언가 잘못되었을 수 있습니다. 코드가 hash()
(또는 md5()
또는 sha1()
) 직접 사용하는 경우 거의 확실하게 잘못된 일을하고있는 것입니다.
라이브러리를 사용하십시오 …
답변
Enough With The Rainbow Tables : Secure Password Schemes 또는 Portable PHP password hashing framework에 대해 알아야 할 정보에 많은 정보가 있습니다.
목표는 암호를 느리게 해시하는 것이므로 암호 데이터베이스를 얻는 사람이 강제로 죽이려고 죽을 것입니다. Bcrypt 는 느리고 매개 변수와 함께 사용하여 속도를 느리게 선택할 수 있습니다.
답변
PHP crypt()
기능을 사용 하고 적절한 복어 소금을 전달 하여 bcrypt로 단방향 해시를 만들 수 있습니다 . 전체 방정식 중 가장 중요한 것은 A) 알고리즘이 손상되지 않았으며 B) 각 암호에 올바르게 소금을 칠하는 것 입니다. 응용 프로그램 전체 소금을 사용하지 마십시오. 레인보우 테이블 하나에서 공격 할 수 있도록 전체 응용 프로그램을 엽니 다.
답변
편집 : 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
비밀번호가 분명하다는 것을 알고 있지만 비밀번호로 ‘비밀번호’를 사용하지 마십시오.
답변
PHP 버전 5.5이 내장 것이다 BCrypt, 기능에 대한 지원 password_hash()
과 password_verify()
. 실제로 이들은 함수 주위의 래퍼 일 뿐이 crypt()
므로 올바르게 사용하기가 더 쉬워집니다. 안전한 무작위 소금 생성을 처리하고 좋은 기본값을 제공합니다.
이 기능을 사용하는 가장 쉬운 방법은 다음과 같습니다.
$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
$isPasswordCorrect = password_verify($password, $existingHashFromDb);
이 코드는 BCrypt (algorithm 2y
)로 비밀번호를 해시하고 OS 랜덤 소스에서 랜덤 솔트를 생성하며 기본 비용 매개 변수 (현재 10)를 사용합니다. 두 번째 줄은 사용자가 입력 한 비밀번호가 이미 저장된 해시 값과 일치하는지 확인합니다.
비용 매개 변수를 변경하려는 경우 비용 매개 변수를 1 씩 늘리면 해시 값을 계산하는 데 필요한 시간이 두 배가됩니다.
$hash = password_hash($password, PASSWORD_BCRYPT, array("cost" => 11));
"cost"
매개 변수 와 달리 "salt"
,이 기능은 이미 암호로 안전한 소금을 만들기 위해 최선을 다하기 때문에 매개 변수 를 생략하는 것이 가장 좋습니다 .
PHP 버전 5.3.7 이상 에는 기능 을 만든 동일한 작성자 의 호환 기능 팩 이 password_hash()
있습니다. 5.3.7 이전의 PHP 버전에 대한 지원이 없습니다 crypt()
와 2y
, 유니 코드 안전 BCrypt 알고리즘. 대신 2a
이전 PHP 버전에 대한 가장 좋은 대안 인로 대체 할 수 있습니다.
답변
현재 생각 : 해시는 가능한 가장 느린 것이 아니라 가장 느린 것이어야합니다. 이것은 레인보우 테이블 공격을 억제 합니다 .
또한 관련이 있지만 예방 조치 : 공격자는 절대로 로그인 화면에 무제한으로 액세스 할 수 없습니다. 이를 방지하려면 : URI와 함께 모든 적중을 기록하는 IP 주소 추적 테이블을 설정하십시오. 5 분 동안 동일한 IP 주소에서 5 회 이상 로그인을 시도한 경우 설명으로 차단하십시오. 두 번째 방법은 은행과 같이 2 계층 비밀번호 체계를 사용하는 것입니다. 두 번째 패스에서 실패에 대한 잠금을 설정하면 보안이 강화됩니다.
요약 : 시간이 많이 걸리는 해시 함수를 사용하여 공격자를 느리게합니다. 또한 로그인에 대한 액세스를 너무 많이 차단하고 두 번째 비밀번호 계층을 추가하십시오.
