루나의 TIL 기술 블로그

위스타그램 3 암호화, 토큰

|

[Mission 5] 회원가입 비밀번호 암호화 적용

  • 유저가 가입할 때 비밀번호를 암호화에서 DB에 저장

Salting(실제 비밀번호 이외에 추가적으로 랜덤 데이터를 더해서 해시값을 계산하는 방법)과 Key Stretching(단방향 해쉬값을 계산 한 후 그 해쉬값을 또 해쉬 하고, 또 이를 반복하는 것을 말한다)을 이용하여 비밀번호를 단방향 암호화하기 위해 만들어진 해쉬함수 bcrypt를 사용한다.

bcrypt 라이브러리 사용하여 비밀번호 암호화

pip install bcrypt 
import bcrypt
password = '1234'
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
print(hashed_password)
b'$2b$12$YFs9rh.1LgJwZuf9ibyjpuLvBoCaGX0MzedFWF2Jo0zU3lMZurZ4a'
#yooyoung > users > views.py

import json, re, bcrypt, jwt #brcypt(암호화)와 jwt(토큰) 임포트
#...
from westagram.settings  import SECRET_KEY #세팅파일에서 시크릿 키 불러오기

class SignUp(View):
    def post(self, request):
        try:
            data = json.loads(request.body)
            hashed_password = bcrypt.hashpw(data['password'].encode('UTF-8'), bcrypt.gensalt())
            #유저에게 받은 새 문자열 비밀번호를 인코딩해서 바이트형태로 이진화하고, 비크립트로 암호화한다.

            #...

            User.objects.create(
                password     =   hashed_password.decode('UTF-8'),
                #DB에 문자열로 저장하기 위해서 암호화된 바이트형태의 비밀번호를 디코딩해준다.
            )

[Mission 6] 로그인 JWT 적용

  • 유저가 입력한 비밀번호 암호화 한후 암호화되서 DB에 저정된 유저 비밀번호와 비교
  • 로그인 성공하면 token을 클라이언트에게 전송 (유저는 로그인 성공후 다음부터는 access token을 첨부해서 request를 서버에 전송함으로서 매번 로그인 해도 되지 않도록 한다)

단방향 해쉬 함수(one-way hash function)는 원본 메시지를 알면 암호화된 메시지를 구하기는 쉽지만 암호화된 메시지로는 원본 메시지를 구할 수 없다. 그래서 비밀번호 확인은 bcrypt.checkpw(입력받은 패스워드, 저장된 암호화된 패스워드)매쏘드를 통해서 한다.

password = '1234'
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
new_password = '1234'
bcrypt.checkpw(new_password.encode('utf-8'),hashed_password)
True
class SignIn(View):
    def post(self, request):
        try:
            #...
            user = User.objects.get(email=data['email'])
            #유저가 반복해서 나오니까 변수로 저장

            if not bcrypt.checkpw(data['password'].encode('UTF-8'), user.password.encode('UTF-8')):
                return JsonResponse({"massage": "INVALID_USER"}, status=401)
                #유저에게 로그인시 입력받은 문자열 비밀번호를 인코딩(이진화)해서 디비에 저장된 비밀번호를 인코딩한 것과 비교해서
                #같으면 트루를 반환한다. 같지 않으면 에러를 반환한다.  

            token = jwt.encode({'user_id': user.id}, SECRET_KEY, algorithm='HS256')
            #입력받은 유저의 아이디로 토큰을 생성

            return JsonResponse({'token': token}, status = 200)
            #이메일과 비밀번호가 일치하는 경우 유저에게 토큰을 전달한다.

        except KeyError:
            return JsonResponse({"massage": "KEY_ERROR"}, status=400)

JSON Web Token JWT

pip install pyjwt
import jwt
SECRET = 'secret' #'랜덤한 조합의 키' 예제이므로 단순하게 'secret'이라고 하겠습니다.
token = jwt.encode({'id' : 1}, SECRET, algorithm = 'HS256') #토큰을 만드는 문법
print(token)
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MX0.-xXA0iKB4mVNvWLYFtt2xNiYkFpObF54J9lj2RwduAI'

인증(로그인)을 통과한 사용자에게 토큰을 발급한다.
인증을 통과한 사용자만 접근하려면 이 토큰을 받아서 다시 우리가 발행한 토큰이 맞는지 확인한다.

header = jwt.decode(token, SECRET, algorithm = 'HS256')
print(header)
{'id': 1}

인증하는 코드는 보통 user app에 utils.py를 만들어서 데코레이터로 구현한다.

깃헙 전체 코드 링크

암호화를 하면 이렇게 비밀번호가 암호화된 테이블을 볼 수 있다.

비밀번호가 암호화되어 저장된 테이블