bcrypt는 어떻게 내장 소금을 가질 수 있습니까?


616

Coda Hale의 기사 "암호를 안전하게 저장하는 방법" 은 다음과 같이 주장합니다.

bcrypt에는 레인보우 테이블 공격을 방지하기 위해 소금이 내장되어 있습니다.

그는 이 논문을 인용하며 , 이는 OpenBSD의 구현에서 bcrypt다음 과 같이 말합니다 :

OpenBSD는 arcfour (arc4random (3)) 키 스트림에서 128 비트 bcrypt salt를 생성하며 커널은 장치 타이밍에서 수집 한 임의의 데이터를 시드합니다.

이것이 어떻게 작동하는지 이해하지 못합니다. 소금에 대한 나의 개념에서 :

  • 저장된 비밀번호마다 달라야하므로 별도의 레인보우 테이블을 생성해야합니다.
  • 반복 가능하도록 어딘가에 저장해야합니다. 사용자가 로그인을 시도 할 때 비밀번호를 입력하고, 비밀번호를 처음 저장할 때와 동일한 소금 및 해시 절차를 반복하고 비교합니다.

bcrypt와 함께 Devise (Rails 로그인 관리자)를 사용할 때 데이터베이스에 솔트 열이 없으므로 혼란 스럽습니다. 소금이 무작위이며 어디에도 저장되지 않은 경우 어떻게 해싱 프로세스를 안정적으로 반복 할 수 있습니까?

간단히 말해서, bcrypt는 어떻게 내장 소금을 가질 수 있습니까?

답변:


789

이것은 bcrypt입니다.

임의의 소금을 생성하십시오. "비용"요소가 사전 구성되었습니다. 비밀번호를 수집하십시오.

솔트 및 비용 요소를 사용하여 비밀번호에서 암호화 키를 파생하십시오. 잘 알려진 문자열을 암호화하는 데 사용하십시오. 비용, 소금 및 암호문을 저장 하십시오 . 이 세 가지 요소의 길이는 알려진 것이기 때문에 하나의 필드에 연결하여 저장하기는 쉽지만 나중에 분리 할 수 ​​있습니다.

누군가 인증을 시도하면 저장된 비용과 소금을 검색하십시오. 입력 암호, 비용 및 소금에서 키를 파생하십시오. 잘 알려진 동일한 문자열을 암호화하십시오. 생성 된 암호문이 저장된 암호문과 일치하면 암호가 일치합니다.

Bcrypt는 PBKDF2와 같은 알고리즘을 기반으로하는 기존 방식과 매우 유사한 방식으로 작동합니다. 주요 차이점은 알려진 일반 텍스트를 암호화하기 위해 파생 키를 사용한다는 것입니다. 다른 체계는 (합리적으로) 키 파생 함수가 돌이킬 수 없다고 가정하고 파생 키를 직접 저장합니다.


데이터베이스에 저장된 bcrypt"해시"는 다음과 같습니다.

$ 2a $ 10 $ vI8aWBnW3fID.ZQ4 / zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa

이것은 실제로 "$"로 구분 된 세 개의 필드입니다.

  • 2a사용 된 bcrypt알고리즘 버전을 식별합니다 .
  • 10비용 요소입니다. 2 키 파생 함수의 10 반복이 사용됩니다 (그런데 충분하지 않습니다. 12 이상의 비용을 권장합니다).
  • vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa소금과 암호문은 수정 된 Base-64에 연결되고 인코딩됩니다. 처음 22자는 솔트의 16 바이트 값으로 디코딩됩니다. 나머지 문자는 인증을 위해 비교할 암호문입니다.

이 예제는 Coda Hale의 루비 구현 문서 에서 발췌 한 것입니다.


7
10의 비용 요소가 충분하지 않은 이유에 대한 자세한 정보를 원하십니까? Grails에서 bcrypt의 비용 요소 / 로그 라운드의 기본값은 10이므로 기본값을 제안하면 업데이트 할 가치가 있음을 알았습니다.
pm_labs

57
bcrypt의 비용 요소는 지수 적이거나 오히려 10의 비용 요소는 2 ^ 10 라운드 (1024)를 의미하고 16의 비용 요소는 2 ^ 16 라운드 (65536)를 의미합니다. 자연스럽게 5-10 초가 걸립니다. 비용 요소 10의 약 64 배가 소요됩니다. 다른 잘못된 정보를 없애기 위해 PHP의 crypt 함수는 c에서 구현 된 unix crypt 라이브러리를 사용합니다.
thomasrutter

3
@TJChambers 맞습니다; 계정에서 비밀번호를 설정할 수 있으면 인증 할 수 있습니다. 비밀번호 해싱은 이러한 공격을 방지하기위한 것이 아닙니다. 암호 테이블에 대한 읽기 전용 액세스 권한을 가진 공격자가 인증하지 못하도록하기위한 것입니다. 예를 들어, 테이블이있는 백업 테이프를 얻습니다.
erickson

8
@LobsterMan 아니요, 실제로는 아닙니다. 비밀을 유지할 수 있다면이 방법을 사용하지 않고 비밀번호 만 저장하면됩니다. 암호 인증 체계는 공격자가 알고있는 모든 것을 발견했다고 가정 한 것입니다. 소금은 각 암호를 개별적으로 공격해야합니다. 암호 테스트에 필요한 계산 노력은 반복에 의해 결정됩니다. 사용자가 좋은 암호를 선택하면 소금이 드러나더라도 안전합니다. 소금을 숨기면 경우에 따라 암호가 틀린 사람에게 도움이 될 수 있지만 먼저 암호 품질 작업을합니다.
erickson

1
@NLV bcrypt 사양에 정의 된 문자열입니다."OrpheanBeholderScryDoubt"
erickson

181

나는 그 문구가 다음과 같이 표현되어야한다고 믿는다.

bcrypt에는 레인보우 테이블 공격을 방지하기 위해 생성 된 해시 에 소금이 내장되어 있습니다.

bcrypt유틸리티 자체는 소금의 목록을 유지하기 위해 표시되지 않습니다. 오히려 솔트는 무작위로 생성되고 함수의 출력에 추가되어 나중에 자바 구현에bcrypt 따라 기억됩니다 . 다시 말해, "해시" 는 해시 만이bcrypt 아닙니다 . 오히려 그것은 해시 소금이 연결되어 있습니다.


20
사이트에 등록하고 비밀번호 "foo"를 선택합니다. Bcrypt"akd2! *"의 임의의 소금을 추가하여 "fooakd2! *"가 생성되어 해시 및 저장됩니다. 나중에 비밀번호 "bar"로 로그인하려고합니다. 내가 맞는지 확인하려면 "barakd2! *"를 해시해야합니다. 솔트가 무작위로 생성 된 경우, 해싱 및 비교하기 전에 솔을 "바"에 다시 추가하는 방법을 어떻게 알 수 있습니까?
Nathan Long

46
@Nathan : bcrypt생성 된 출력 (데이터베이스에 저장 됨)에서 소금을 다시 추출하는 방법을 알고 있습니다. 인증시기가 bcrypt되면 원래 출력을 해시 및 솔트 구성 요소로 분리합니다. 솔트 구성 요소는 사용자가 입력 한 수신 비밀번호에 적용됩니다.
Adam Paynter

22
Nathan Long의 의견에 대답하기 위해, 이것을 생각하는 좋은 방법은 소금이 비밀이 아니라는 것입니다. 이것이 위에서 지적한 답변 중 하나로서 소금이 bcrypt 함수의 출력에 포함되는 이유입니다. 소금은 레인보우 테이블을 방지하기 위해 존재합니다. 레인보우 테이블은 일반적인 암호 목록이거나 다른 암호의 무차별 대입 등이지만 해시됩니다. salt가 없으면 데이터베이스 A의 비밀번호에 대한 해시는 데이터베이스 B의 비밀번호에 대한 해시와 동일합니다. Salt는 데이터베이스를 도난당한 사람이 비밀번호를 해독 (해시 해제)하기 어려운 해시 값만 변경합니다.
Joseph Astrahan

11
@Nathan 그러나 공격자가 모든 암호에서 알려진 소금을 제거한 다음 그와 함께 표를 만들 수 있습니까?
Oscar

3
이것이 내가 이해하는 방법입니다. 아이디어는 모든 암호에 고유 한 소금이 있다는 것입니다. 암호 해시에 포함 된 소금은 해커가 모든 암호에 대해 레인보우 테이블을 만들어야합니다. 적당한 데이터베이스에는 많은 시간이 걸립니다. 공격자의 속도를 늦추고 무차별 대입을 무의미하게 만드는 것이 전부입니다.
PVermeer

0

더 명확하게하기 위해

등록 / 로그인 방향->

비밀번호 + 솔트는 비용, 솔트 및 비밀번호에서 생성 된 키로 암호화됩니다. 우리는 그 암호화 된 값을 호출합니다 cipher text. 그런 다음 소금을이 값에 연결하고 base64를 사용하여 인코딩합니다. 그것에 비용을 첨부하면 다음에서 생성 된 문자열입니다 bcrypt.

$2a$COST$BASE64

이 값은 결국 저장됩니다.

침입자가 암호를 찾으려면 어떻게해야합니까? (다른 방향 <-)

공격자가 DB를 제어 할 수있는 경우 공격자는 쉽게 base64 값을 해독 한 다음 소금을 볼 수 있습니다. 소금은 비밀이 아닙니다. 비록 무작위입니다. 그런 다음 그는 암호를 해독해야합니다 cipher text.

더 중요한 것은 :이 프로세스에는 해시가 없으며 CPU 비용이 많이 드는 암호화-암호 해독이 있습니다. 따라서 무지개 테이블은 여기서 덜 관련이 있습니다.


-2

Spring Security의 PasswordEncoder 인터페이스 문서에서 가져온 것입니다.

 * @param rawPassword the raw password to encode and match
 * @param encodedPassword the encoded password from storage to compare with
 * @return true if the raw password, after encoding, matches the encoded password from
 * storage
 */
boolean matches(CharSequence rawPassword, String encodedPassword);

즉, 다음 로그인시 사용자가 다시 입력 할 rawPassword와 일치해야하며 이전 로그인 / 등록 중에 데이터베이스에 저장되는 Bcrypt 인코딩 비밀번호와 일치해야합니다.


이것은 전혀 질문에 대한 답이 아닙니다 ... 그것은 bcrypt가 어떻게 내장 소금을 가질 수 있는지에 대해서는 아무 것도 말하지 않습니다
spencer.sm
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.