암호화 (Encryption)

minch·2021년 7월 26일
1

Django

목록 보기
10/16
post-thumbnail

유저가 회원가입을 실행할때,

{
	'user' : 'user1010',
    'password' : 'password123'
}

위와 같은 형태로 정보를 보내고 이 형태 그대로 DB에 저장을 한다면,

DB가 해킹을 당하거나, 관리자가 유저의 정보를 알고 악용을 할 수가 있다.

이러한 문제점을 해결하기 위해, 비밀번호는 꼭 암호화를 한 후에 DB에 저장을 해야한다.

암호화 (encryption)

비밀번호 암호화에는 복호화(암호화 > 비밀번호)를 할 수 없는 단방향 해시 함수(one-way hash function)가 일반적으로 쓰인다.

해시 함수를 이용하여 비밀번호를 암호화된 메시지인 digest로 바꾸는 방법이다.

예를 들어 hash256이라는 함수를 사용하여,

>>> import hashlib
>>>
>>> a = hashlib.sha256()
>>> a.update(b'a')
>>> a.hexdigest()
'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb'
>>>
>>> b = hashlib.sha256()
>>> b.update(b'ab')
>>> b.hexdigest()
'fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603'

'a''ab'를 이진법으로 바꾸고 hash256 해쉬함수를 통해 변환하면 위와 같이 큰 차이가 있는 것을 확인할 수 있다.

하지만 이런 방법도 미리 해시값들을 계산해 놓은 'Rainbow table'을 이용하거나,
임의의 문자열을 해시 함수에 값을 대입하여 생성한 digest와 해킹 대상의 digest를 비교하는 방법으로 충분히 해킹이 가능하다.

이런 문제점을 해결하기 위해 bcrypt를 통해 SaltingKey Stretching 으로 보안을 강화할 수 있다.

Salting이란? 기존 비밀번호에 임의의 문자열을 추가하는 방법

Key Stretching은 해시된 결과를 다시 입력으로 해시를 반복하는 방법

예를들어 sha256으로 해시한다고 했을때,

'a' + 'salt'

=> 1978080463c89468c84aeb4c135be4b65a7961153897cf90dda025e3536f1c56

=> 3c412bef7d8d84bbeaf72df4498b27447d589457a1532888dbdf11edada623b2

=> 4cce89fa5bdbdd638d357145fecda5d51a256289ea810f0b8df31c0d90bf561a

...

와 같은 식으로 계속 반복하는 것이다. (이 반복횟수가 엄청 많다.)

그래서 views.py에 기존 비밀번호에 적용을 해보면

import bcrypt

...
class SignUpView(View):
    def post(self, request):
        data = json.loads(request.body)
        
        try:
            ...
            hashed_password = bcrypt.hashpw(data['password'].encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
            # 먼저 받아온 password를 encode하고 bcrypt 처리한 다음, 다시 decode

            User.objects.create(
                name         = data['name'],
                email        = data['email'],
                password     = hashed_password,
                phone_number = data['phone_number'],
                age          = data['age'],
                gender       = data['gender'],
                birth_date   = data['birth_date']
            )

위에서 bcrypt로 암호화한 비밀번호를 decode해서 DB에 저장을 해야한다.

decode를 하는 이유는 DB에는 str형태로 저장이 되기 때문에,

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

        try:
            if not User.objects.filter(email=data['email']).exists():
                return JsonResponse({"MESSAGE":"INVALID_USER"}, status=401)
            
            user = User.objects.get(email=data['email'])

            check_password = bcrypt.checkpw(data['password'].encode('utf-8'), user.password.encode('utf-8'))
            #받아온 password를 encode하고, DB내 password를 꺼내와 encode하고 실행
            
            if not check_password:
                return JsonResponse({"MESSAGE":"INVALID_USER"}, status=401)
	    return JsonResponse({"MESSAGE":"SUCCESS"}, status=200) 

user.password DB의 비밀번호를 불러와서 다시 encode를 해야 bcrypt.checkpw()가 정상적으로 실행된다.

참조
(https://snippets-save-us.tistory.com/44)

0개의 댓글