티스토리 뷰

golang

비밀번호 안전보관: bcrypt 를 알아보자

사용자 fistful 2020. 1. 28. 18:33
반응형

개요

 

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

비밀번호 보관에 특화된 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 동작 원리

 

 

이미지 출처

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 값이다.

 

예시를 하나만 보자

- 링크: https://stackoverflow.com/a/6833165/6513756

- cost 10 이면 2^10 돌린 것이다.

- 버전과 cost 정보를 나머지가 salt + cipher text 인데 Base-64 인코딩 것이다.

- 앞부분 22 문자를 decode 해주면 16 바이트 salt 된다. 나머지가 cipher text 이다.


 

결과물은

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 실행

 

코드를 잠깐 보자

 

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 분리해서 만들어보기

 

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 만들어보자

 

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

아래와 같이 실행시마다 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 써보자

 


 

참고. Golang bcrypt

 

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

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

반응형
댓글
댓글쓰기 폼