TIL) 비밀번호 암호화

Jiwon Lee·2022년 6월 7일
0

TIL

목록 보기
8/19

인증 (Authentication)

인증이란, 접근을 시도하는 유저가 DB 상에 저장된 유저인지 아닌지를 검증하는 Django의 모델 폼을 말한다. (유저가 로그인 폼에 작성한 정보가 유효한지 검증하는 단계.)

유저 비밀번호 암호화

Q. 왜 DB상의 비밀번호를 암호화해서 저장하는가?
A1. 유저의 정보들은 내부 개발자나 인력들에게도 비밀시 되어야 한다.
A2. 해킹을 당하게 되면, DB 안의 내용들이 전부 노출이 되기 때문.

  • 비밀번호를 암호화할 때엔, 단방향 해쉬 함수(one-way hash function)가 일반적으로 쓰인다.
    단방향 해쉬 함수는 본래 입력한 데이터 값을 암호화시킨 다이제스트(digest)를 만든다. 본래 입력값을 알면 암호화된 데이터값을 구하기는 용이하지만, 암호화된 데이터값으로는 본래의 값을 찾기 힘들기에 단방향(one-way)라고 한다.

"hello world" 라는 문자열을 sha256이라는 해쉬 함수를 사용하면,

'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'

이러한 값이 나오게 된다.

그런데 여기서 같은 값이지만 "hello world2"라는 문자열을 넣어 다시 해쉬 함수를 사용해 보면,

'f93c20b30171d10e773dc2a2d8ed59524b25baddf381b83fcc4ec40f50bedb33'

이렇게 전혀 다른 값이 나오는 것을 확인할 수 있다.

이러한 효과를 avalance라고 하는데, 비밀번호의 해쉬 값에 대한 해킹을 어렵게 만드는 하나의 요소이다.

Bcrypt

bcrypt는 비밀번호 암호화에 사용되는 알고리즘을 제공하는 라이브러리를 말한다.
우선, "pip install" 명령어로 bcrypt를 설치하고, "python"을 콘솔에 입력하여 쉘을 띄운다.
** 보통은 "python manage.py shell"명령어로 파이썬 쉘을 켜지만, 간단한 것들은 "python"으로 사용해도 무방하다.
가장 처음으로 bcrypt를 import하고 실행을 한다.

hashpw()

bcrypt.hashpw()는 'password'와 'salt'라는 인자값이 요구된다. 여기서 'password'는 유저가 입력한 비밀번호, 'salt'는 bcrypt가 제공하는 gensalt()라는 함수를 통해 만들어진 무작위의 값들을 의미한다.

>>> salt = bcrypt.gensalt()
>>> salt
b'$2b$12$D.Jrsgwzux/RHobPUtpQqu'

gensalt()로 salt를 만들었다. (salt 값 앞의 'b'는 데이터 타입이'bytes'임을 의미한다.)

>>> bcrypt.hashpw("hellowlizzy", salt)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/homebrew/Caskroom/miniconda/base/lib/python3.8/site-packages/bcrypt/__init__.py", line 80, in hashpw
    raise TypeError("Unicode-objects must be encoded before hashing")
TypeError: Unicode-objects must be encoded before hashing

이대로 hashpw()에 인자를 넣고 돌렸는데 에러가 발생했다. 유저에게 받은 비밀번호의 데이터 타입이 'utf-8'로 되어있기 때문이다. 'utf-8'을 'bytes'로 바꾸려면 다시 encoding을 해주면 된다.

>>> pw = "hellolizzy".encode('utf-8')
>>> pw
b'hellolizzy'

비밀번호가 bytes 타입으로 바뀐 것을 확인하고 다시 암호화를 재진행해본다.

>>> hs_pw = bcrypt.hashpw(pw, salt)
>>> hs_pw
b'$2b$12$D.Jrsgwzux/RHobPUtpQqufn/QgIHuZ5EuuIK/0EywnQd4rqUa1UK'

우리가 모델링을 할 때에, password의 필드 타입은 Charfield로, str타입을 받게 되어있다. 지금껏 우리가 암호화를 해서 받은 password 타입은 bytes이기 때문에, 다시 string으로 바꾸는 작업을 해야 DB에 저장이 가능하다.

>>> dc_pw = hs_pw.decode('utf-8')
>>> dc_pw
'$2b$12$D.Jrsgwzux/RHobPUtpQqufn/QgIHuZ5EuuIK/0EywnQd4rqUa1UK'

checkpw()

checkpw는 말 그대로 password를 비교해주는데, 유저가 입력한 비밀번호와 DB상에 저장되어있는 비밀번호를 인자로 받아 비교하여 확인해준다.

>>> password = "hellolizzy"
>>> bcrypt.checkpw(password, dc_pw)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/homebrew/Caskroom/miniconda/base/lib/python3.8/site-packages/bcrypt/__init__.py", line 120, in checkpw
    raise TypeError("Unicode-objects must be encoded before checking")
TypeError: Unicode-objects must be encoded before checking

유저가 입력한 비밀번호와 DB에 저장된 비밀번호의 데이터 타입이 또 발목을 잡는다. 이전처럼 encode를 하여 bytes 타입으로 변환시킨 후 다시 진행해본다.

>>> encoded_pw = password.encode('utf-8')
>>> encoded_db_pw = dc_pw.encode('utf-8')
>>> bcrypt.checkpw(encoded_pw, encoded_db_pw)
True

checkpw()의 결과는 boolean(True/False)값이 나오기 때문에 두 인자의 값이 같다면, True가 출력되는 것이 정상이다.

0개의 댓글