비밀번호는 개인 정보 중 하나로 사용자가 기억해야 하는 정보이다.
사용자가 인스타그램에 회원가입을 하게 되면 아이디와 이메일, 그 외 다른 개인 정보를 입력하고 회원가입 신청을 하게 되는데, 그 때 서버에 사용자의 정보가 저장된다. 그러나 비밀번호가 평문 그대로 저장되면 나중에 서버에 문제가 생겼을 때 비밀번호가 그대로 노출될 수도 있다.
따라서 서버에 비밀번호를 저장할 때는 암호화(encryption)를 해서 저장해야 한다.
비밀번호 암호화 방법에는 단방향 해쉬 함수가 주로 쓰인다. 즉, 복호화를 하기 힘들게 하여 암호화된 비밀번호에서 원래 평문의 비밀번호를 유추할 수 없게 하기 위함이다. 그러나 이 방법도 완벽한 암호화는 아니다. 왜냐하면 Rainbow 테이블이라는 미리 해쉬값을 계산해 놓은 테이블을 이용하여 보안을 뚫어버리는 공격이 존재하기 때문이다.
쉽게 얘기하면 스무고개를 하듯 수많은 평문 비밀번호를 입력한 값과 암호화된 값들을 저장해 놓고 유추가 가능하기 때문이다.
따라서 암호화를 보완할 방법을 찾아야 했다. 바로 salting과 Key Stretching 이다.
음식에 소금을 뿌릴 때 랜덤한 크기의 알갱이가 랜덤한 방향으로 뿌려져 나온다는 점에서 착안한 salting이라는 방식은 임의의 짧은 문자열 데이터를 비밀번호에 추가한다.
단방향 해쉬값을 계산하고 그 결과값을 또 해싱하며 여러번 반복하는 것을 의미한다.
해쉬 함수중 salting과 key stretching을 잘 구현해 낸 함수가 bcrypt이다. python에서 라이브러리로 사용할 수 있다.
pip install bcrypt
pip3로 bcrypt를 설치하고 해당 라이브러리를 import하여 사용한다.
>>> import bcrypt
>>>
>>> mySalt = bcrypt.gensalt()
>>> password = 'abcd1234'
>>>
>>> encoded_password = password.encode('utf-8')
>>> hashed_password = bcrypt.hashpw(encoded_password, mySalt)
>>> decoded_password = hashed_password.decode('utf-8')
>>>
>>> mySalt
b'$2b$12$isamWjzCrMoRO1Vt6ZvHjO'
>>> encoded_password
b'abcd1234'
>>> hashed_password
b'$2b$12$isamWjzCrMoRO1Vt6ZvHjOPYbqLoLd9M0Zr0Cnnn2Gim1BIq9u882'
>>> decoded_password
'$2b$12$isamWjzCrMoRO1Vt6ZvHjOPYbqLoLd9M0Zr0Cnnn2Gim1BIq9u882'
위에 코드에서 보면 encode를 하면 앞에 b
가 붙고 decode를 하면 앞에 b
가 떨어져 나온다. 이것의 차이는 'bytes 타입인가 str타입인가' 이다.
encode를 하면 bytes 타입으로 바뀌고 decode는 str 타입으로 바뀐다. 그래서 그냥 인코딩이 귀찮으면 앞에 b를 붙여주면 된다.
이 부분은 jwt와 조금 다르기 때문에 jwt에서 다시 설명할 것이다
jwt는 json web token
으로 json 기반 표준 web token이다. 구조는 다음과 같다.
유저가 로그인에 성공하면 토큰을 발급받게 되는데 토큰의 용도는 현재 사용자가 정당한 사용자인지 식별하는 용도이다. 사용자가 요청을 보냈을 때 권한이 있는 사용자인지 판별할 수 있다.
토큰을 보면 이렇게 생겼다. 본인이 만든 토큰과 비교해 보면
잘 안보이겠지만 .
으로 나눠진 총 3개의 구역을 볼 수 있다.
첫번째 구역이 header이고 두번째 구역이 payload, 세번째가 서명이다. 사용자의 id와 같은 데이터는 payload에 들어가므로 payload를 잘 분해하여 고유값을 얻어내야 한다.
pip install pyjwt
터미널에서 pyjwt를 설치하고 python으로 넘어간다.
>>> import jwt
>>>
>>> encoded_jwt = jwt.encode({'id' : 1}, 'secret_key', 'algorithm')
>>> encoded_jwt
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MX0.iLAYBcIhadoRhCcOId9MYBPFB5ElP1yG1_5dquIExCo'
>>> type(encoded_jwt)
str
'secret_key'
에는 본인이 설정한 secret_key를 적으면 되고 'algorithm'
에는 사용할 해쉬 알고리즘을 적으면 된다. 참고로 pyjwt에서 jwt를 발급하면 타입이 str이라고 나오는데 버전이 바뀌면서 bytes 타입에서 str 타입으로 바뀌었으니 참고하여 사용하면 된다.
치헌님 주말에도 열공하시더니!! 정리 깔끔하게 잘하셨군요!!!