저번에 bcrypt을 해서, 패스워드를 hashing 하는 것을 썼습니다.

 

 이전 글에서 올려놨던 것과 비교하면 달라진 것이 딱 하나 있습니다. 인자가 하나 추가되었다는 점입니다. String pw 말고도, len을 받는 것이 하나 추가되었는데요. 이것은 strength를 받기 위해 쓰였습니다. 예를 들어, 이 값이 15라면 32768 round, 32768번 반복을 의미합니다.

 

 17이라면 2^17인 131072번 반복을 의미합니다. 당연하게도, len에 지수 비례합니다. 이것은 어렵지 않게 postman 등으로 테스트 했을 때 나옵니다. 이렇게, strength를 정해 준 다음에는, 암호화된 패스워드와, 넘겨준 패스워드를 비교해서 match가 되면 accept를 떨어트려 주고, 그렇지 않으면 wrong을 출력합니다. 여기서 하나 질문. pw가 abcde이고 len이 15인 요청을 여러 번 날려 봅시다.

 

 

 그러면 결과로 떨어지는 string이 이건 $2a$15$mAPpdc... 이렇게 시작합니다.

 

 그런데 이건 $2a$15$0m3uzBhVi... 로 시작합니다. 결과가 매번 요청할 때 마다 달라짐을 알 수 있어요. round 수가 같음에도 불구하고요. 이는 round 말고도 중간에 뭔가가 랜덤하게 들어갔다는 의미입니다. 그런데, 이걸 가지고 어떻게 match가 되는지를 판단할까요? 사실 랜덤하게 hash 값이 생성되기만 하면 의미가 없을 겁니다. 무슨 의미가 있을까요? hash 특성상, 인풋이 조금만 바뀌어도 결과가 확 달라지는데요. salt의 목적이 그것입니다. 그리고, t가 나왔을 때, 이것은 어떤 것을 hashed 했는지 알아내는 것이 쉬운 것은 아닙니다. "abcde"라는 것을 알고 있지만, salt를 모르면 힘들 겁니다.

 

 분명, 제가 모르는 다른 뭔가가 있을 겁니다. match 메서드를 디버그를 타고 들어가 봅시다.

 


 plaintext하고 hashed가 있습니다. 여기서 hashed는 비밀 번호로 알려져 있는 것이 hash가 된 결과입니다. $2a$17$... 이런 식으로 시작하는데요. 여기서 17은 2^17번 round를 돌림을 의미합니다.

 

 

 hashpw 안으로 들어가 봅시다.

 

 salt는 암호화가 된 무언가를 의미합니다. "$2a$..." 이런 꼴로 되어 있었으니, salt.charAt(2)는 '$'가 아닙니다. 'a'입니다. 따라서, 317번째 줄로 가게 되는데요.

 

 valid 하다면, off 값은 4가 되겠네요.

 

 다음에 오는 것은 round 수입니다. $17$과 같은 것들입니다. salt가 "$2a$17$..." 이런 식으로 시작한다면, salt.substring(off, off + 2)는 "17"이 될 겁니다. 그리고 off+3은 17 뒤에 있는 $ 뒤에 있는 것을 가리킬 겁니다. real_salt는 여기서부터 22개의 문자를 뽑아낸 것인데요. 디버거를 보겠습니다.

 

 보면 CIKq...WO까지가 real_salt라고 되어 있어요. 정확하게 글자수를 세어 보면 22자임을 알 수 있어요.

 

 salt 부분과 hash 부분을 분리한 것입니다. 결론은 salt 값으로 쓴 것을 "abcde"가 암호화 된 string을 통해 얻어왔다는 것입니다.

 


 salt를 알고 raw값을 알면 어떻게 하면 될까요? 단지, 우리가 넘겨준 password와 해쉬가 된 것에서, 파싱을 통해 알아낸 salt를 가지고 Hashing을 시켜보면 됩니다. 이 부분을 간단하게 flow로 그려보면 아래와 같습니다.

 

 "abcde"와 얻어낸 salt 정보를 가지고 bcrypt에 집어넣으면 hashed라는 결과가 떨어집니다. 이것을 적절히 조합해서 원래 암호화된 문자열과 비교해서 같으면 통과시키면 되고, 아니면 불일치라고 하면 되겠네요.

 

 이렇게 해서, salt와 rs가 같으면 match가 되었다고 리턴하면 됩니다. 큰 가지만 설명하다 보니, base64 디코딩하고, 인코딩하는 로직은 빼먹었습니다. 이 글에서는, bcrypt로 암호화를 할 때 마다 salt가 랜덤하게 덧붙여 지는데 어떻게 암호화된 데이터를 가지고 raw pw를 보내면, match 되는지 판단하는지에 대해서 다루었습니다. 결론은, 암호화된 string 중에서 일부분은 salt로 쓰고 있어서, 그 데이터를 얻어온 뒤에, raw pw와 같이 보내서 encrypt를 시켜서 비교한다는 것입니다.