비밀번호 암호화 구현과 JWT(1)

.·2020년 5월 18일
1

Coding

목록 보기
21/33

1. bcrypt/PyJWT 설치

회원가입 시 비밀번호는 데이터베이스에 저장하기 전 암호화가 필요하다.

bcrypt 는 비밀번호 암호화에 사용되는 알고리즘을 제공하는 라이브러리이다.
가상환경 내에 설치하도록 한다. PyJWT 또한 동시에 설치 하였다.

$ pip install bcrypt PyJWT

# 가상환경 bcrypt_test를 활성화 & 프로젝트디렉토리로 이동
 bcrypt_test  $ pip install bcrypt # 비크립트 설치
Collecting bcrypt
  Downloading bcrypt-3.1.7-cp34-abi3-macosx_10_6_intel.whl (53 kB)
     |████████████████████████████████| 53 kB 203 kB/s

...중략

Installing collected packages: six, pycparser, cffi, bcrypt
Successfully installed bcrypt-3.1.7 cffi-1.14.0 pycparser-2.20 six-1.14.0

pip freeze 로 설치된 내역들을 확인할 수 있다.

2. 비밀번호 암호화

bcrypt의 hashpw() 메소드로 비밀번호 암호화를 진행하는 테스트를 위해 터미널에서 python 을 입력하여 파이썬 인터프리터를 실행해 본다.

실습 사진을 첨부해 본다.

>>> import bcrypt
>>> bcrypt.hashpw('5602',bcrypt.gensalt())
>>> bcrypt.hashpw('5602',bcrypt.gensalt())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/teddyjung/miniconda3/envs/westagram/lib/python3.7/site-packages/bcrypt/__init__.py", line 61, in hashpw
    raise TypeError("Unicode-objects must be encoded before hashing")
TypeError: Unicode-objects must be encoded before hashing

오 해쉬 전에 유니코드 오브젝트는 인코딩 되어야 한다 라는데 그 말인 즉슨 내가 비밀번호라고 지정한 '5602' 가 사실 문자열이기 때문에 문자열 비밀번호를 bytes 로 변환한 뒤 암호화를 진행해야 된다는 타입 에러이다.
왜 유니코드는 안되?라고 생각할수 있는데 이유는 그저 bcrypt 인자는 bytes로 입력을 받기때문에~ 비밀번호를 byte형으로 바꿔주자.

2-1. 형변환 방법 1번

>>> bytes('5602','utf-8')
b'5602'

>>> a = bytes('5602','utf-8')
>>> type(a)
<class 'bytes'>

bytes 로 형변환을 해주고, 변수 a에 대입한 다음 변수 a의 타입을 확인해 보았다. 역시나 바이트로 바뀌어 있었다. (피자 한 바이트 하고 싶..읍..)

2-2. 형변환 방법 2번

>>> b = '5602'
>>> type(b)
<class 'str'>
>>> b.encode('utf-8')
b'5602'
>>> c = b.encode('utf-8')
>>> c
b'5602'
>>> type(c)
<class 'bytes'>

임의의 변수에 문자열을 할당해 주고 b.encode('utf-8') 로 확인하니 바로 위 형변환 방법1번과 동일한 결과를 확인할 수 있었다.

2-3. 안보고 무시하고 넘어가도 되는 것 (나빼고)

아니 비밀번호는 문자열이야? 숫자 아닌감? 난 숫자로 넣을꺼 따옴표 뺄꺼! 빼액!

>>> bytes(5602, 'utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: encoding without a string argument

조용히 문자열이라는 점을 깨닫도록 하자.

자 그럼 원래 하려던 거 마저 해야지.

2-4. 비밀번호 암호화 재시도

>>> hashed_password = bcrypt.hashpw('5602'.encode('utf-8'),bcrypt.gensalt())
>>> hashed_password
b'$2b$12$LGEQ6ljIJgMiT7QTPyWdw.9TF4PRq.5r9SBCLE/bnZB26K4nWxCju'

참고로 bcrypt.gensalt() 에서의 gensalt() 는 소금 생성기로써 비밀번호를 외계어로 바꿔치기 하는 것에 더해 소금을 뿌려서 비밀번호 + 더미 를 생성해 암호화를 강력하게 만들어 준다는 느낌으로 이해하면 되겠다.

암호화 된 비밀번호를 출력해보니 b'~~~' 와같이 byte형 타입 인 것을 확인 할 수 있다.
이제 이 암호화 된 비밀번호를 DB에 저장해보자!

2-5. 암호화 된 비밀번호를 DB 에 저장하기

데이터베이스 의 유저 정보를 담는 테이블 그러니까 account 앱 내의 models.py 파일을 확인해보자.
아마 이럴껄? 아님 말고..

password = model.CharField(max_length=200)

해쉬 비밀번호는 bytes형 이기 때문에 DB에 저장 할 수 없다. 저장할 수 있도록 문자열로 변환하자.

>>> decode_hashedpw = hashed_password.decode('utf-8')
>>> decode_hashedpw
'$2b$12$LGEQ6ljIJgMiT7QTPyWdw.9TF4PRq.5r9SBCLE/bnZB26K4nWxCju'
>>> type(decode_hashedpw)
<class 'str'>

3. 로그인 적용하기

로그인이 구현되는 과정을 알아보자.

  1. POST 요청으로 아이디와 비밀번호를 받는다.
  2. 아이디정보를 DB에 존재하는지 찾는다.
  3. 비밀번호를 암호화(회원가입 시 비번암호화 로직과 동일)한다
  4. DB의 비밀번호 값과, 새로입력받은 해시비밀번호와 값이 같은지 확인한다.

3-1. checkpw()

bcrypt에서는 비밀번호 정보를 비교할수 있는 checkpw()메서드를 제공한다. 위 과정을 더 단순하게 구현 할 수 있다. 먼저 bcrypt.checkpw()는 어떤 인자를 받는 지 확인하자.

파이썬 쉘이든 뭐든 쉘을 잘 써 버릇해야 되는구나 싶다.

>>> bcrypt.checkpw()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: checkpw() missing 2 required positional arguments: 'password' and 'hashed_password'

그 두가지 포지셔널 인자는 바로
1. password : 로그인 시 입력받은 비밀번호
2. hashed_password : DB에 저장된 해시 암호화 된 값

>>> bcrypt.checkpw('5602'.encode('utf-8'), hashed_password)
True

bcrypt.checkpw()는 Bool 값을 리턴한다. 비밀번호 확인인 True 가 출력 되었을때 토큰을 발행하도록 진행하면 마무리 된다.

4. summary

  • 소금은 소금생성기로 랜덤한 값을 받자 bcrypt.gensalt()
  • 비밀번호를 암호화 할땐 bcrypt.hashpw(바이트형 비밀번호, 소금생성기의 랜덤값)
  • 비밀번호 확인 할땐 bcrypt.checkpw(바이트형 입력받은 비밀번호, 바이트형 DB에 저장된 해쉬 값)
  • encode : 바이트 값으로 변환 해쉬용 값을 바꿀때 hashed_password.encode('utf-8')
  • decode : 문자열로 변환 > DB에 저장할 때 ! hashed_password.decode('utf-8')
profile
.

0개의 댓글