TIL - 인증 & 인가

Magit·2020년 4월 15일
0

TIL

목록 보기
13/16

인증과 인가에 대해 알아보기 위해서 쉘에서 한 번 연습해보도록 해보겠다.

bcrypt를 통한 암호화

일단 인증, 즉 암호화를 위해서 필요한 bcrypt를 설치해야 한다.

pip install bcrypt

설치 후에 당연히 사용하려면 import bcrypt 를 해야한다.

>>> import bcrypt
>>> a = '1234'	# a라는 변수에 1234라는 문자열을 담아본다.


>>> bcrypt.hashpw()
TypeError: hashpw() missing 2 required positional arguments: 'password' and 'salt'
# hashpw()는 2개의 arguments가 필요하다. 비밀번호와 소금이다!


>>> bcrypt.hashpw(a, bcrypt.gensalt())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/magrfs/miniconda3/envs/auth-exam/lib/python3.8/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
# bcrypt.gensalt()는 해쉬의 복잡도를 결정해주는 요소이며 디폴트 값은 12이다. 지금은 아무것도 넣지 않았으니 12이다.
# 말하는대로 암호화할 패스워드를 담은 변수 a와 소금을 넣었는데 에러가 떴다.
# 해싱을 하기전에 인코드가 필요하다는 의미이다. 암호화 함수는 bytes 타입에서만 작동하기에 해싱하기전에 bytes 타입으로 변환해줘야 한다. 현재 암호는 그냥 문자열이다!


>>> type(a)
<class 'str'>
# 실제로 a의 타입을 알아보니 str 클래스 타입이라고 한다. 그렇다면 인코딩을 해보자!


>>> a.encode('utf-8')
b'1234'
>>> bytes(a, 'utf-8')
b'1234'
# bytes 로 바꾸는 방법은 두가지가 있다. 보통 encode를 많이 쓴다.


>>> b = a.encode('utf-8')
>>> type(b)
<class 'bytes'>
# 인코딩한 값을 b 라는 변수에 담았다. 타입을 확인해보자 bytes 로 변했다!


>>> hashed_password = bcrypt.hashpw(a.encode('utf-8'),bcrypt.gensalt())
>>> hashed_password
b'$2b$12$4Fu2187QzJomODmqMXzVM.3jSPdqm9EClOhEvkkgkzWmJGsHuPJ26'
>>> type(hashed_password)
<class 'bytes'>
# 해싱을 하고서 출력해봤다. 추가로 타입도 확인해보자 역시 bytes로 되있다!
# 암호화된 비밀번호는 데이터형식의 bytes이기 때문데 다시 디코딩을 해줘서 데이터베이스에 저장해줘야한다.
# 생각해보라 우리가 model.py에서 패스워드를 저장할 때 필드의 타입을 무엇으로 설정했었는가?
# 바로 VARCHAR였다. 그러므로 디코드를 해서 타입을 맞춰야한다!


>>> hashed_password.decode('utf-8')
'$2b$12$4Fu2187QzJomODmqMXzVM.3jSPdqm9EClOhEvkkgkzWmJGsHuPJ26'
# 앞에 b가 사라진게 보이는가? 디코딩되었다!


>>> c = '1234'
>>> bcrypt.checkpw(c.encode('utf-8'), hashed_password)
True
>>> c = '12345'
>>> bcrypt.checkpw(c.encode('utf-8'), hashed_password)
False
# 1234, 12345를 인코딩해서 해싱한 패스워드와 일치하는지 확인해봤다.
# 1234는 역시 True가 나온다. 즉, 해싱할 때 같은 값이 나온다는 것이다.
# 12345는 당연히 False가 나온다!

여기까지가 bcrypt를 통해서 비밀번호를 암호화하는 과정이었다.
다음으로 JWT를 이용한 토큰 발행에 대해서 알아보자.


토큰 발행하기

유저가 아이디와 비밀번호를 입력해 로그인 버튼을 누르면 서버는 db에 저장된 것과 비교한다. 인증이 완료되면 유저에게 토큰을 발행해준다. 토큰은 유저 브라우저의 쿠키나 세션스토리지에 저장되어 서버에 데이터를 요청할 때 마다 토큰을 담아서 정보를 요청하고, 서버는 해당 토큰을 검증한 뒤 데이터를 전송하게된다.

pip install PyJWT
import jwt
# 설치는 PyJWT를 해야한다! 하지만 import는 jwt를 한다.

이제 다시 한 번 쉘에서 갖고 놀아보자.

>>> import jwt
>>> payload = { 'user_id':1 }
# import를 한 뒤 payload라는 변수에 값을 담아봤다.

>>> jwt.encode()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: encode() missing 2 required positional arguments: 'payload' and 'key'
# jwt도 인코드 과정을 거쳐야되는데, 인자로 payload와 key가 필요하다고 한다. 사실 여기서 
알고리즘을 정하는 인자를 줄 수도 있다.

>>> a = jwt.encode(payload, 'secret')
>>> a
b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxfQ.J_RIIkoOLNXtd5IZcEwaBDGKGA3VnnYmuXnmhsmDEOs'
# 인코딩을 하고 출력해봤다.

>>> a.decode('utf-8')
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxfQ.J_RIIkoOLNXtd5IZcEwaBDGKGA3VnnYmuXnmhsmDEOs'
# 이것 역시 디코딩!


>>> a = jwt.encode(payload, 'secret', algorithm='HS512')
>>> a.decode('utf-8')
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjoxfQ.Ut_TonHBOesY0AEsk_iY3MUxjploFjxgsjAX3HCvI23tdYgzCdOlkSljQXjsVzqn01lyz-jtWDXh6k__lyftUw'
# 위에 말했듯이 인코딩을 할 때, 알고리즘을 정할 수 있다!


>>> a = jwt.encode(payload, 'secret', algorithm='HS512')
>>> b = jwt.decode(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/magrfs/miniconda3/envs/auth-exam/lib/python3.8/site-packages/jwt/api_jwt.py", line 91, in decode
    decoded = super(PyJWT, self).decode(
  File "/Users/magrfs/miniconda3/envs/auth-exam/lib/python3.8/site-packages/jwt/api_jws.py", line 155, in decode
    self._verify_signature(payload, signing_input, header, signature,
  File "/Users/magrfs/miniconda3/envs/auth-exam/lib/python3.8/site-packages/jwt/api_jws.py", line 223, in _verify_signature
    raise InvalidSignatureError('Signature verification failed')
jwt.exceptions.InvalidSignatureError: Signature verification failed
>>> b = jwt.decode(a, 'secret', algorithm='HS512')
>>> b
{'user_id': 1}
# a에 HS512라는 알고리즘으로 인코딩을 했고 b로 디코딩을 하려고 했더니 에러가 났다.
# 즉, 디코딩을 하기 위해서 시크릿키와 알고리즘까지 알고 있어야한다.
# 디코딩을 했더니 처음에 저장했던 { 'user_id': 1 } 을 알 수 있다.
# 즉, 페이로드에 사용자의 정보를 저장하면 안된다는 것이다. 만약이란 일이 일어날 수 있기에!

대략적으로 알아봤지만, 아직 개념이 부족한 느낌이다.
조금더 열심히 공부해보자!

profile
이제 막 배우기 시작한 개발자입니다.

1개의 댓글

comment-user-thumbnail
2023년 10월 24일

감사합니다.

답글 달기