티스토리 뷰

study/Django

Django - 암호화 (bcrypy)

xoxowo 2022. 7. 12. 21:50

전 포스팅의 회원가입 구현 회원 로그인 구현에서 사용자의 중요 정보(비밀번호)를 암호화하지 않았기 때문에 이번에는 bcrypy 라이브러리를 이용하여 어떤 방식으로 암호화를 하고 암호화한 사용자 정보를 어떻게 매칭 시켜 로그인이 되는지 과정을 정리해봤다. 

(이 포스팅에서 사용한 bcrypy 외에 장고 공식 문서에도 비밀번호를 어떤 방식으로 암호화하는지, 또 어떤 라이브러리가 있는지 확인 할 수 있다.)

 

brcypy공식 문서에서 사용 방법을 보고 나름대로 이해한 것을 간략하게 정리했다. 

 

→ bcrypy 설치 및 import 

→ 클라이언트로부터 받은 정보 중 비밀번호를 데이터 테이블에 저장 시 암호화하여 저장 (회원가입)

→ 회원 로그인 시 입력 받은 비밀번호와 암호화하여 저장한 비밀번호를 encode하여 매칭 후 결과 반환 (회원 로그인)

 

 

★ 파이썬의 문자열은 기본적으로 유니코드를 사용하기 때문에 문자열인 비밀번호를 brcypt 라이브러리에서 사용하기 위해선 utf-8 방식으로 바이트 인코딩(encode)해야한다고한다.

 

비밀번호 해쉬화 하는 과정 (공식 문서 참고)

# 유저가 입력한 비밀번호 1234 는 'str' 타입이다
password = '1234'

# 이 패스워드를 'byets' 타입으로 바꿔주면서 해쉬화를 한다
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())

# 그리고 데이터테이블에 넣을 때는 byests 타입이 아닌 다시 문자열로 넣어야하기 때문에 decode하여 넣는다
data_password = hashed_password.decode('utf-8')

# 받은 정보를 인코딩하여 해쉬화하면서 바로 디코딩할 수 있다. 
# 다만 내부 코드에 따라 비밀번호 검증 후 정보를 저장하기 때문에 작업을 분리할 수도 있다
hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')

해쉬화된 비밀번호를 확인하는 과정 (공식 문서 참고)

# 유저가 입력한 비밀번호 1234 는 'str' 타입
password = '1234'

# 회원가입 시 저장된 비밀번호는 디코딩하여 저장했었음
hash_password = brcypt.hashpw(password, bcrypt.gensalt()).decode('utf-8')

#입력한 비밀번호('str')와 저장된 비빌번호('str')는 각각 다시 인코딩해서 비교함
if bcrypt.chekpw(password.encode('utf-8'), hash_password.encode('utf-8') :
	print("It Matches!")
else :
	print("It Dose not Match!")

 

 

→ bcrypy 설치 및 import 

먼저, 가상 환경에서 작업을 하고 있었기 때문에 가상 환경에 bcrypy을 설치해주었다.

% pip install bcrypt

설치 후 암호화를 처리해야 하는 views.py에 import 해줘야 한다

# users/views.py
import json

import re
import bcrypt # <- 외부 라이브러리 추가

 

 회원가입 시 사용자 정보 (비밀번호 암호화 후 저장)

기존에 작성했던 views.py에 암호화 과정 추가

# users/views.py
import bcrypt 
  """ ...  생략 """

class SignUpView(View):
    def post(self, request):
        data = json.loads(request.body) 

        try :
            name         = data['name']
            email        = data['email']
            password     = data['password']
            phone_number = data['phone_number']
		# 이메일 정규식 표현 사용 검증
            if not re.match('^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', email) :
                return JsonResponse({"message": "Invalid email format"}, status = 400)   
		# 비밀번호 정규식 표현 사용 검증
            if not re.match('^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{8,}$', password):
                return JsonResponse({"message": "Invalid password format"}, status = 400)
		# 중복 이메일 검증  
            if StoreUser.objects.filter(email=email).exists():
                return JsonResponse({"message": "Already registered Email"}, status = 400)   
               # 이메일 및 비밀번호 정규표현식 검증 이후 받은 비밀번호를 해쉬화하면서 디코딩하여 변수에 저장
            hash_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')            
		# 위에 작업한 코드를 데이터 생성 시 password 에 넣어줌
            StoreUser.objects.create(
                name          = name,
                email         = email,
                password      = hash_password, 
                phone_number  = phone_number,
            )
            return JsonResponse({"message": "SUCCESS"}, status = 201)
            
        except KeyError :
            return JsonResponse({"message": "KEY_ERROR"}, status = 400)

회원가입 성공 후

회원가입 시 입력한 유저 정보 중 암호화될 부분인 password = '123qwe!!' 

http -v post 127.0.0.1:8000/users/signup email='e@naver.com' password='123qwe!!' name='a' username='aaa' phon_number=0001

성공적으로 데이터가 생성되었을 경우 password값인 123qwe!! 가 디코딩 되어 들어간 것을 확인

# mysql
+----+------+--------------+----------+--------------------------------------------------------------+
| id | name | email        | username | password                                                     |           
+----+------+--------------+----------+--------------------------------------------------------------+
| 19 | a    | e@naver.com  | aaaa     | $2b$12$JbvBP41zOdSA0JZNTJmRpeXGBp22e17XE8pmTCDYKSR5oSG5h.oo2 |

 

 회원 로그인 시 사용자 정보 확인 

기존에 작성했던 SignInView클래스에 비밀번호 매칭 과정추가

 

# users/views.py
import bcrypt 
  """ ...  생략 """

class SignInView(View):
    def post(self, request):
        data = json.loads(request.body)

        try: 
            email    = data['email']
            password = data['password']
	# 사용자 정보가 있는지 확인 있을 경우 user 변수에 모든 정보 담아오기
            if StoreUser.objects.filter(email=email).exists() :
                user = StoreUser.objects.get(email=email) 
	# checkpw( 사용가 입력한 비밀번호 인코딩, 데이터테이블에 저장되어있는 디코딩된 비밀번호 다시 인코딩) 매치확인        
                if bcrypt.checkpw(password.encode('utf-8'), user.password.encode('utf-8')):
                    access_token = jwt.encode({"id": user.id }, SECRET_KEY, ALGORITHM)

                    return JsonResponse({"message": access_token }, status = 200) 
                
                else :
                    return JsonResponse({"message": "INVALID_USER"}, status = 401)    
            
            return JsonResponse({"message": "INVALID_USER"}, status = 401)               

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

회원 로그인

회원 로그인 시 입력한 password= '123qwe!!' 

http -v 127.0.0.1:8000/users/signin email='u@naver.com' password='123qwe!!'

http 통신에서 입력받은 password 값 '123qwe!!'으로 로그인 성공 (jwt 토큰 사용으로 반환되는 메세지가 토큰 값이 들어감)

POST /users/signin HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 48
Content-Type: application/json
Host: 127.0.0.1:8000
User-Agent: HTTPie/3.2.1

{
    "email": "u@naver.com",
    "password": "123qwe!!"
}

HTTP/1.1 200 OK
Content-Length: 108
Content-Type: application/json
Cross-Origin-Opener-Policy: same-origin
Date: Tue, 12 Jul 2022 10:06:09 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.8.13
Vary: Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "message": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTJ9.
    RQynyR3xCDuT_lOQmD9Uc7aZse6CrEIespUXgpEh5rI"
}

 

 

 


내용 전문 및 출처 - bcrypy, django

'study > Django' 카테고리의 다른 글

Django -path Variable  (0) 2022.07.24
Django - JWT 발급  (0) 2022.07.13
Django - 회원 로그인 구현  (0) 2022.07.10
Django - 회원가입 구현  (0) 2022.07.10
Django - QuerySet 다루기 (초보)  (0) 2022.07.09
댓글