Bcrypt 비밀번호 비교 - Bcrypt bimilbeonho bigyo

개요

사용자의 비밀번호를 그대로 보관하는 것은 위험하다.

비밀번호 보관에 특화된 bcrypt 를 알아보자.

참고링크

- 링크: https://auth0.com/blog/hashing-in-action-understanding-bcrypt/

- 링크: https://d2.naver.com/helloworld/318732

TL;DR

- 빠르게, 보안도 신경썼다는 티를 내면서 구현하려면 bcrypt 를 사용하자

- 보안에 좀더 민감한 곳이라면 scrypt 를 쓰자

- 현 시점 비밀번호 저장의 끝판왕은 Argon2id 이다.

비밀번호를 보관하자.

1) Plaintext 를 저장한다면 데이터베이스가 털리면 바로 끝이다.

- 이녀석 비밀번호는 asdf1234 이구나.

2) Hashing 을 한번 해주고  그걸 저장하면 낫지만 rainbow table 을 이용한 공격을 한다면?

- 미리 많이 쓰는 비밀번호와 그 hash 값들을 테이블로 만들어놓으면,

- 유출된 hash 값에서 비밀번호을 역으로 찾아낼 수 있다.

참고링크: http://bit.ly/36z3ujp

1) Dictionary attack: 많이 쓰는 비밀번호를 하나씩 넣어보자

2) Brute force attack: 사용가능한 문자열 내에서 무작위로 넣어보자

3) Rainbow Table: 위에 이미 언급

3) 비밀번호에 Salt 를 추가한 다음 hashing 하자

- salt 만들기와 hashing 이 한방에 이루어지면 금상첨화

SHA 가 있는데 bcrypt 는 왜 만들었지?

SHA2, SHA3 는 너무 빠른게 오히려 단점이다.

- 빨리빨리 rainbow table 을 만들 수 있다.

- 최신 CPU, GPU라면 SHA-256 을 초당 백반, 천만번씩 수행할 수 있다.

한계들

1) constant password length: 잉간이 기억하고 쓸 수 있는 비밀번호라면 길이의 한계를 가진다.

2) rapidly evolving hardware: 컴터가 빨라진다고 해싱 속도가 빨라지면 난감하다.

bcrypt 는

key setup phase 라는 일종의 막대한 전처리 요구로 느리게 만든 Blowfish 란 녀석의 특징에

반복횟수를 변수로 지정가능하게 하여 작업량 (=해싱시간) 을 조절할 수 있게 해주었다.

→ 원하는 만큼 속도를 조절가능한 hash 함수란 말이다.

bcrypt 동작 원리

Bcrypt 비밀번호 비교 - Bcrypt bimilbeonho bigyo

이미지 출처

:  https://auth0.com/blog/hashing-in-action-understanding-bcrypt/

1단계: Key setup phase

eksblowfish 함수

- expensive key schedule Blowfish, Blowfish 의 key setup algorithm 강화버전이다.

- 파라미터는 원하는 비용 (= 연산량, 소요시간), salt, password

- password 를 가지고  key stretching 하며 연산속도도 늦춰줌

2단계: Operation phase

- plaintext ↔ ciphertext 로  encrypt/decrypt 하는 단계

1) OrpheanBeholderScryDoubt 는 192 비트 (= 24 바이트) 이며 1단계의 결과이다. 

2) 이걸  eksblowfish 가 동작하는 ECB mode 로 64번 암호화한다.

3) 암호화 루프 64번의 결과 + salt 128 비트 (=16 바이트) 등을 합친 것이 최종 결과이다.

- 아래 예시를 보면 이해가 쉬울 것이다.

결과 해시 알아보기

온라인에서 bcrypt 로  암호화를 해보았다. 링크: https://www.devglan.com/online-tools/bcrypt-hash-generator

1) $2b 는 bcrypt 의 버전정보이다.

2) $10 은 10 round 를 의미하며, cost 정보를 의미한다. 이게 클 수록 연산의 cost 가 증가하는 것이다.

3) 까지가 cost 정보를 통해 생성된 salt 이다. 이 값은 매 실행시마다 변한다.

4) 나머지가 hash 값이다.

이 결과물은

1) preimage attack (역상공격) 에 대해 저항성을 가진다. http://bit.ly/2RSFhPC

- 해시함수의 출력값이 같은 새로운 입력값을 찾는 공격

2) 충분히 큰 salt 공간을 가지고 있어서 rainbow attack 에 저항성을 가진다.

3) 변경이 가능한 cost (연산량, 연산시간)

실전을 통해 알아보기

- node.js 의 bcrypt 패키지를 이용하면 salt 생성과 hash 생성을 분리해서 볼 수 있어서 이해에 도움이 된다.

- 코드는 링크에 나와 있다. https://auth0.com/blog/hashing-in-action-understanding-bcrypt/

우선 cost 가 소요시간에 미치는 영향을 테스트 해보았다.

1) node.js. 설치

2) npm install bcryptjs 로 패키지 설치

3) 아래 코드 작성 후 node bcrypt1.js 실행

Bcrypt 비밀번호 비교 - Bcrypt bimilbeonho bigyo

코드를 잠깐 보자

1) bcryptjs 라는 패키지를 가져온 다음

2) 비밀번호를 asdf1234 로 가정했다.

3) bcrypt.hashSync() 함수를 실행하는데

- 파라미터는 plainTextPassword 와 10 부터 20까지의 saltRounds 값이다.

- 즉 cost 인 salt round 를 바꿔가며 소요시간을 측정해본 것이다.

결과를 보면

cost 가 10일때는 0.1초도 안되던 시간이

cost 를 20으로만 주어도 62초가 넘게 걸리는 것을 알 수 있다.

→ cost 가 30이 넘어가면 웬만한 시스템에서는 1년이 넘어 걸릴 수도 있다.

이러한 결과값으로 방정식을 역으로 만들어서 운영하는 시스템에 적절한 cost 를 계산해낼 수 있다.

이것 만으로도  불안하다면 정답은 two-factor, 혹은 multi-factor 인증이다.

salt 와 hash 분리해서 만들어보기

Bcrypt 비밀번호 비교 - Bcrypt bimilbeonho bigyo

1) saltRounds 값을 10으로 하여 bcrypt.genSalt() 함수로 salt 를 만들었다.

-  $2a$10$h3a5Cw4Amac.LDNjOWLAQu

- bcrypt 버전은 $2a 이니 2a 이고

- saltRounds 는 $10 이니 10 rounds 란걸 알 수 있다.

→ 중요한 것은 매번 실행시마다 salt 값이 바뀐다는 것!

2) bcrypt.hash() 함수에 plainTextPassword 와 salt 를 넣어서  hash 를 만들었다.

- hash 앞부분에 salt 가 포함되어 있는 것을 알 수 있다. 

Salt: $2a$10$DTKVAPansJtga9/d.K770e

Hash: $2a$10$DTKVAPansJtga9/d.K770eSgvzoh/mW.1vMrd0FzzHArYlqaTQT9m

Salt: $2a$10$z7wcUjnXVMGoaURjXmgvXO

Hash: $2a$10$z7wcUjnXVMGoaURjXmgvXOQe65u0NnEaEDLWP7hGlSopyUgAVc342

한방에 hash 를 만들어보자

Bcrypt 비밀번호 비교 - Bcrypt bimilbeonho bigyo

코드에 따로 설명이 필요할 것 같지는 않다.

아래와 같이 매 실행시마다 salt (=Hash 앞부분)과 전체 결과가 바뀌는 것을 알 수 있다.

Hash: $2a$10$IzweIEGkW4A1uzMNnPH/GegLN8bAb2dXe7Gg9QKDFmzIQ9gvTuQYK

Hash: $2a$10$uz0MJWTUvwQtdYertA9ibe/R0LOh9s1eH/3zxdev8vDzRgiLoqDgK

Hash: $2a$10$krPc1D50B0XR6E1PGvSZ8eoPAvKc9yqH3HwCJzUtCbhGq57gixEPC

위 코드와 이 코드를 보면 무엇을 알 수 있을까?

1. 이 코드처럼, plainTextPassword  와 saltRounds 만 넣으면 hash 를 생성해서 저장해둘 수 있다.

2. 이제 이렇게 등록된 사용자가 비밀번호를 넣어 로그인을 시도한다면?

1) 입력한 비밀번호와 저장된 비밀번호 hash 의 앞 salt 로 hash 를 만들어보면

비밀번호가 맞는지 확인할 수 있겠지?

실제 validate 를 해보자

plainTextPassword 는 asdf1234 로 하고

hash 는 위에서 생성한 $2a$10$IzweIEGkW4A1uzMNnPH/GegLN8bAb2dXe7Gg9QKDFmzIQ9gvTuQYK 를 써보자

Bcrypt 비밀번호 비교 - Bcrypt bimilbeonho bigyo

참고. Golang bcrypt

링크: https://godoc.org/golang.org/x/crypto/bcrypt

링크만 보아도 hash 생성과, 이후 입력된 비밀번호와의 비교를 알 수 있다.