https://velog.io/@eagle5424/%EC%9D%B8%EC%A6%9D-%EC%9D%B8%EA%B0%80
인증 : 유저의 identification을 확인하는 절차를 의미
인가 : 유저가 요청하는 request를 실행할 수 있는 권한이 있는 유저인가를 확인하는 절차 이다.
bycrpt : 비밀번호 암호화 시에 사용하는 라이브러리를 의미한다. bycrpt에 관련해서 직접 알고리즘을 만들어서 사용할 수도 있지만 보안을 위해 이미 만들어진 것을 사용하는 것을 추천한다.
jwt(Json Web Token) : 로그인 즉 request 할 수 있는 권한을 가지고 있는 사람이라는 것을 알기 위해, Request의 headers 부분에 메타데이터를 보내는데, 이 메타 정보를 의미한다. 일종의 티켓이라고 생각하면 편하다.
비밀번호를 암호화 하고 다시 복호화 할 수 있는 알고리즘
(※ 복호화 : 아날로그 신호를 디지털 단위로 변환한 것을 부호화 라고 하는데, 이 부호화를 역순으로 수행하여 부호화 되기 전으로 되돌리는 것을 의미한다. 디코딩(decoding)이라고도 한다. )
대표적으로 대칭키(비공개키), 비대칭키(공개키) 가 있다. 각각 장단점이 존재합니다.
대칭키(비공개키) 방식
비대칭키(공개키) 방식
대칭형 암호화 방식은 훌륭한 암호화 방식이지만, 결정적인 문제가 존재한다. '키 배송'에 관련된 문제로 어떻게든 전달해야하지만, 키 배송과정중에 털리게 되면 아무리 뛰어난 알고리즘을 사용하더라도 속절없이 털리게 된다. 이런 단점을 해결하기 위해 나온것이 비대칭키 방식이다.
비대칭키 방식에 필요한 키는 두 개이다. 이 때 키를 A, B라고 가정했을때, 키 A로 암호화한 암호문은 키 B로만 복호화 할 수 있고, 키 B로 암호화한 암호문은 키 A로만 복호화 할 수 있다. 그래서 이 두개의 키 중 하나의 키만 비밀로 보호하고 다른 하나의 키는 공중에게 공개해도 관계가 없다. 어짜피 공개키로 암호화한 암호문은 개인키(비밀키)를 가진 사람만이 복호화 할 수 있기 때문에, 공개키로 암호화를 시킨뒤 개인이 가지고 있는 비밀키를 통해서 복호화를 하게 되면 키 배송시에 해킹당할 위험이 아예 사라지게 된다.
하지만 이러한 과정은 대칭키에 비해서 속도가 현저히 느리다는 단점이 있습니다. 따라서 현실적으로는 비대칭형 암호를 사용해서 대칭형 암호의 키를 배송하고, 실제 암호문은 대칭형 암호를 사용하는 식으로 상호보완적으로 이용하는 것이 일반적이다.
이런 단방향 해시의 대표적인 라이브러리가 bycrpt 입니다.
pip install bycrpt
가상환경에 bycrpt를 설치를 해준다.
로그인 시에 암호화를 해줘야 하기 때문에
create 시에 password 에 bcrpyt.hashpw()를 통해 암호화를 해준다.
코드을 보면 request.body로 들어온 password 의 데이터를 encode를 통해 부호화를 해누고, gensalt()를 통해 salting까지 해주는 것을 볼 수 있다.
하지만 이렇게 되면 데이터 베이스에 바이트 타입으로 저장이 되게 된다.
이런 식으로 암호화된 비밀 번호 앞에 'b' 가 붙어서 바이트 타입으로 저장이 되는 것을 알수 있다. 하지만 models.py에서 만들때 binaryField로 만들지 않으면 문제가 된다. 나중에 로그인 View를 진행하는데 있어 checkpw에서 db에 저장되어 있는 비밀번호와 입력한 비밀번호를 비교하는 과정이 이루어진다. 그런데 이상태로 db에 저장이 되게 된다면 바이트 타입이라고 표시한 b 마저도 비밀번호로 인식이 이루어 지게 되서 오류가 나게 된다.
이를 방지하기 위해 암호화가 이루어진 비밀번호 끝에 decode('utf-8'), string 타입으로 변형 시켜주는 복호화를 한 번 해준 뒤에 저장을 해준다.
password의 앞에서 'b'가 없어진 채로 저장이 되어있는 것을 볼 수 있다.
FE가 보내준 데이터의 password를 바이트 타입으로 바꾸고, db에 저장되어 있는 비밀번호를 다이 바이트 타입으로 바꾸어서 비교를 해준다.
코드
if bcrpyt.checkpw(data['password'].encode('utf-8'), user.password.encode('utf-8)):
data['passowrd'].enode('utf-8) -> FE에서 받은 비밀번호를 부호화
user.password.encode('utf-8') -> string 타입으로 저장되어 있는 db를 부호화 해서 비교
두개가 일치하지 않는다면 400에러
일치한다면!!!
access_token발급 -> JWT발행
access_token = jwt.encode({'id': user.id}, 'secret', algorithm='HS256').decode('utf-8')
JWT를 부호화(encode)를 해서 token에 정보를 담아줄 것이다. Json 으로 통신을 할것이기때문에 담아야 할 내용(user.id)를 dict의 형태로, 암호화키 = 'secret', 암호화한 algorithm='HS256'을 담아주고 로그인에성공했다는 티켓인 token을 JsonResponse에 담아서 return 해준다.
이런식으로 password를 암호화를 해서 저장하고 login시에 저장되어있는 password를 비교해서 token을 주는 형식으로 bcrpyt를 사용해서 암호화를 해보았다.