여기서 문제는 기본적으로 엔트로피 문제입니다. 이제부터 살펴 보겠습니다.
문자 당 엔트로피
바이트 당 엔트로피 비트 수는 다음과 같습니다.
- 16 진수 문자
- 비트 : 4
- 값 : 16
- 72 자 엔트로피 : 288 비트
- 영숫자
- 비트 : 6
- 값 : 62
- 72 자 엔트로피 : 432 비트
- "공통"기호
- 비트 : 6.5
- 값 : 94
- 72 자 엔트로피 : 468 비트
- 전체 바이트
- 비트 : 8
- 값 : 255
- 72 자 엔트로피 : 576 비트
따라서 우리가 행동하는 방식은 우리가 기대하는 캐릭터 유형에 따라 다릅니다.
첫 번째 문제
코드의 첫 번째 문제는 "pepper" 해시 단계가 16 진수 문자를 출력한다는 것입니다 (네 번째 매개 변수가hash_hmac()
가 설정되지 않았기 때문에).
따라서 후추를 해싱하면 암호에 사용할 수있는 최대 엔트로피를 2 배 ( 가능한 576에서 288로) 효과적으로 줄일 수 있습니다. 비트).
두 번째 문제
그러나 처음 sha256
에는 256
약간의 엔트로피 만 제공합니다 . 따라서 가능한 576 비트를 256 비트로 효과적으로 줄일 수 있습니다. 정의상 해시 단계 * 즉시 * 는 암호에서 가능한 엔트로피 의 50 % 이상을 잃
습니다 .
SHA512
사용 가능한 엔트로피를 약 12 % 만 줄일 수있는 로 전환하여이 문제를 부분적으로 해결할 수 있습니다. 그러나 그것은 여전히 중요하지 않은 차이입니다. 이 12 %는 순열 수를 1.8e19
. 그것은 큰 숫자입니다. 그리고 그것이 그것을 줄이는 요인입니다 ...
근본적인 문제
근본적인 문제는 72자를 초과하는 세 가지 유형의 암호가 있다는 것입니다. 이 스타일 시스템이 그들에게 미치는 영향은 매우 다를 것입니다.
참고 : 이제부터는 SHA512
원시 출력 (16 진수 아님) 을 사용하는 후추 시스템과 비교한다고 가정 합니다.
높은 엔트로피 임의 암호
이들은 암호에 대해 큰 키를 생성하는 암호 생성기를 사용하는 사용자입니다. 무작위 (인간 선택이 아닌 생성됨)이며 캐릭터 당 엔트로피가 높습니다. 이러한 유형은 상위 바이트 (문자> 127) 및 일부 제어 문자를 사용합니다.
이 그룹은 해당 해시 함수는 것입니다 크게 가용 엔트로피로 감소 bcrypt
.
다시 말하겠습니다. 높은 엔트로피, 긴 암호를 사용하는 사용자의 경우 솔루션 은 암호의 강도를 측정 가능한 양만큼 크게 줄입니다. (72 자 암호의 경우 62 비트 엔트로피 손실, 긴 암호의 경우 그 이상)
중간 엔트로피 임의 암호
이 그룹은 공통 기호가 포함 된 비밀번호를 사용하지만 상위 바이트 또는 제어 문자는 사용하지 않습니다. 입력 가능한 암호입니다.
이 그룹의 경우 약간 더 많은 엔트로피 잠금 해제 (생성하지 않고 bcrypt 암호에 더 많은 엔트로피를 허용). 내가 약간 말할 때 나는 약간을 의미합니다. 손익분기 점은 SHA512에있는 512 비트를 최대로 사용할 때 발생합니다. 따라서 피크는 78 자입니다.
다시 말하겠습니다. 이 암호 클래스의 경우 엔트로피가 부족해지기 전에 6 자만 추가로 저장할 수 있습니다.
낮은 엔트로피 비 랜덤 암호
무작위로 생성되지 않은 영숫자 문자를 사용하는 그룹입니다. 성경 인용구와 같은 것. 이 문구는 문자 당 약 2.3 비트의 엔트로피를 가지고 있습니다.
이 그룹의 경우 해싱을 통해 더 많은 엔트로피를 잠금 해제 할 수 있습니다 (생성하는 것이 아니라 bcrypt 암호 입력에 더 많이 맞출 수 있음). 손익분기 점은 엔트로피가 부족하기 전에 약 223 자입니다.
다시 말해 봅시다. 이 암호 클래스의 경우 사전 해싱은 확실히 보안을 크게 향상시킵니다.
현실 세계로 돌아 가기
이러한 종류의 엔트로피 계산은 실제 세계에서 그다지 중요하지 않습니다. 중요한 것은 엔트로피를 추측하는 것입니다. 이것이 공격자가 할 수있는 일에 직접적인 영향을 미칩니다. 그것이 당신이 극대화하고 싶은 것입니다.
엔트로피를 추측하는 연구는 거의 없지만 제가 지적하고 싶은 몇 가지 사항이 있습니다.
연속적으로 72 개의 올바른 문자를 무작위로 추측 할 확률은 매우 낮습니다. 이 충돌을 겪는 것보다 파워 볼 복권에 21 번 당첨 될 가능성이 더 큽니다. 그게 우리가 말하는 숫자가 얼마나 큰지입니다.
그러나 우리는 통계적으로 그것을 우연히 발견하지 못할 수도 있습니다. 구문의 경우 처음 72자가 동일 할 확률은 임의의 암호보다 훨씬 높습니다. 그러나 여전히 사소한 수준입니다 (문자 당 2.3 비트를 기준으로 Powerball 복권을 5 번 이길 가능성이 더 높습니다).
거의
실제로는 중요하지 않습니다. 누군가가 처음 72자를 맞히고 후자의 문자가 큰 차이를 만들 가능성이 너무 낮아 걱정할 필요가 없습니다. 왜?
글쎄, 당신이 문구를 취한다고 가정 해 봅시다. 그 사람이 처음 72자를 정확하게 맞출 수 있다면 정말 운 이 좋거나 (가능성이 낮음) 일반적인 문구입니다. 일반적인 문구 인 경우 유일한 변수는 제작 시간입니다.
예를 들어 보겠습니다. 성경에서 인용을합시다 (다른 이유가 아니라 긴 텍스트의 일반적인 소스이기 때문에) :
이웃집을 탐 내지 마십시오. 당신은 이웃의 아내 나 그의 하인이나 여종, 그의 소나 당나귀, 또는 이웃에게 속한 것을 탐 내서는 안됩니다.
180 자입니다. 73 번째 문자는 g
두 번째 neighbor's
. 그렇게 많이 추측했다면에서 멈추지 않고 nei
나머지 구절을 계속할 것입니다 (비밀번호가 사용되는 방식이기 때문에). 따라서 "해시"가 많이 추가되지 않았습니다.
BTW : 저는 성경 인용문을 사용하는 것을 절대적으로지지하지 않습니다. 사실 정반대입니다.
결론
먼저 해싱하여 긴 암호를 사용하는 사람들을 많이 돕지는 않을 것입니다. 당신이 확실히 도울 수있는 몇몇 그룹. 일부는 확실히 다칠 수 있습니다.
그러나 결국에는 그 어느 것도 지나치게 중요하지 않습니다. 우리가 다루고있는 숫자는 단지입니다 WAY 너무 높다. 엔트로피의 차이는 크지 않을 것입니다.
bcrypt를 그대로 두는 것이 좋습니다. 방지하려는 공격이 발생하는 것보다 해싱을 망칠 가능성이 더 높습니다 (말 그대로 이미 해봤고 그 실수를 한 첫 번째 또는 마지막이 아닙니다).
나머지 사이트 보안에 집중하십시오. 그리고 등록시 암호 상자에 암호 엔트로피 미터를 추가하여 암호 강도를 표시합니다 (암호가 너무 길어서 사용자가 변경하려는 경우 표시).
그것은 적어도 내 $ 0.02 (또는 $ 0.02 이상)입니다 ...
"비밀"후추를 사용하는 한 :
bcrypt에 하나의 해시 함수를 제공하는 것에 대한 연구는 말 그대로 없습니다. 따라서 "peppered"해시를 bcrypt에 입력하면 알려지지 않은 취약점이 발생하는지 여부는 확실하지 않습니다 ( hash1(hash2($value))
충돌 저항 및 사전 이미지 공격과 관련된 심각한 취약점을 노출 할 수 있음을 알고 있습니다).
이미 비밀 키 ( "후추") 저장을 고려하고 있다는 점을 고려할 때 잘 연구되고 이해 된 방식으로 사용하는 것은 어떻습니까? 해시를 저장하기 전에 암호화하지 않는 이유는 무엇입니까?
기본적으로 암호를 해시 한 후 전체 해시 출력을 강력한 암호화 알고리즘에 제공합니다. 그런 다음 암호화 된 결과를 저장하십시오.
이제 SQL-Injection 공격은 암호 키가 없기 때문에 유용한 정보를 유출하지 않습니다. 키가 유출되면 공격자는 일반 해시를 사용하는 것보다 낫지 않습니다 (증명할 수 있지만 후추 "사전 해시"가 제공되지 않는 것).
참고 : 이렇게하려면 라이브러리를 사용하십시오. PHP의 경우 Zend Framework 2의 패키지를 강력히 권장 Zend\Crypt
합니다. 실제로 현재 시점에서 내가 추천 할 유일한 것입니다. 강력한 검토를 거쳐 모든 결정을 내립니다 (매우 좋은 것입니다) ...
다음과 같은 것 :
use Zend\Crypt\BlockCipher;
public function createHash($password) {
$hash = password_hash($password, PASSWORD_BCRYPT, ["cost"=>$this->cost]);
$blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
$blockCipher->setKey($this->key);
return $blockCipher->encrypt($hash);
}
public function verifyHash($password, $hash) {
$blockCipher = BlockCipher::factory('mcrypt', array('algo' => 'aes'));
$blockCipher->setKey($this->key);
$hash = $blockCipher->decrypt($hash);
return password_verify($password, $hash);
}
그리고 모든 알고리즘을 잘 이해되고 잘 연구 된 방식 (적어도 상대적으로)으로 사용하고 있기 때문에 유용합니다. 생각해 내다:
가장 우둔한 아마추어부터 최고의 암호 전문가에 이르기까지 누구나 자신이 깰 수없는 알고리즘을 만들 수 있습니다.