-. 인증이란 유저의 Identification을 확인하는 절차로, 유저의 아이디와 비밀번호를 확인합니다.
-. 인증을 하기 위해선 유저의 아이디와 비밀번호를 생성할 수 있는 기능이 필요합니다.
먼저 로그인 절차는 다음과 같습니다.
access token
을 클라이언트에게 전송access token
을 첨부해서 request를 서버에 전송함으로서 매번 로그인 해도 되지 않도록 한다단방향 해시 함수 문제점
※ 레인보우 공격(rainbow attack) - 인식 가능성
동일한 메시지가 언제나 동일한 다이제스트를 갖는다면 해커가 전처리(pre-computing) 된 다이제스트를 다량 확보한 다음 탈취한 다이제스트와 비교해 원본 메시지를 찾아내거나 동일한 효과의 메시지를 찾을 수 있습니다. 이와 같은 다이제스트 목록을 레인보우 테이블(rainbow table)이라 한다. 게다가 다른 사용자의 패스워드가 같으면 다이제스트도 같으므로 한꺼번에 모두 정보가 탈취될 수 있습니다.
※ 무차별 대입 공격(brute force attack) - 속도
해시 함수는 원래 짧은 시간에 데이터를 검색하기 위해 설계된 것으로 해시함수의 빠른 처리 속도로 인해 해커는 매우 빠른 속도로 임의의 문자열의 다이제스트와 해킹할 대상의 다이제스트를 비교할 수 있습니다. (MD5를 사용한 경우 일반적인 장비를 이용하여 1초당 56억 개의 다이제스트를 대입 가능)
단방향 해시 함수 보완
※ 솔팅(Salting)
패스워드에 임의의 문자열인 salt를 추가하여 다이제스트를 생성하는 것으로, 같은 패스워드라도 각기 다른 salt가 들어가 다이제스트가 다르게 생성되어 rainbow table을 무의미하게 만들어 줍니다. (salt는 최소 128bit 정도는 되어야 안전합니다)
※ 키 스트레칭(key stretching)
해시를 여러 번 반복하여 시간을 늘림으로써 무차별 대입 공격(brute force attack)에 대비하는 것입니다. 쉽게 말하자면, 패스워드의 다이제스트를 생성하고, 생성된 다이제스트를 입력값으로 하여 또 다이제스트를 생성하고... 이를 반복하여 다이제스트를 생성하는 식입니다. 만약 원래대로라면 1초에 56억 개를 대입하여 비교할 수 있는데 키스트레칭을 적용하여 1초에 5번 정도만 비교할 수 있게 설정한다면 하나의 다이제스트를 해킹하는 데 오랜 시간이 걸릴 것입니다.
Bcrypt 암호화
salting과 key stretching을 모두 구현한 해쉬 함수 중 가장 널리 사용되는 것이 bcrypt입니다. 암호화를 위해서 hashpw 함수를 사용하는데 사용법은 아래와 같습니다. (pip install bcrypt 설치)
Import bcrypt
bcrypt.hashpw(pw, bcrypt.gensalt())
그런데 기준 문자열 자리에 유니코드를 그대로 넣어주면 에러가 발생합니다.
(유니코드(Unicode)는 전 세계의 모든 문자를 컴퓨터에서 일관되게 표현하고 다룰 수 있도록 설계된 산업 표준)
따라서 encode를 하여 type : str → bytes로 변환한 후 값을 넣어줘야 합니다.
(bcrypt 에서 encoding 역할은 문자열을 → bytes로 변환시켜 주는 역할을 하고,
decoding 역할은 bytes → 문자열로 변환시켜 주는 역할을 합니다.)
bcrypt.gensalt()로 불규칙한 salt를 만들어 해쉬된 비밀번호를 만드는 방식인데, 아래와 같이 비밀번호가 생성되는 것을 볼 수 있습니다. 여기서 앞의 b는 bytes 형식이라는 것을 의미하는데 이 상태에서는 DB에 값을 저장할 수 없어 다시 str 타입으로 변환하기 위해 hashed_pw.decode('utf-8')과 같이 decode를 해주어야 합니다.
hashed_pw = bcrypt.hashpw (pw.encode('utf-8'), bcrypt.gensalt())
hashed_pw
b'$2b$12$9471p.XRlBD81NW0ZcMSbuAi0FdpuFbaMyraTpy8vcyMgtkthULh6'
hashed_pw.decode('utf-8') #DB 저장을 위한 decode(str 타입으로 변경)
Bcrypt 비밀번호 검증
로그인 할 때 비밀번호 검증을 위해 입력된 비밀번호와 DB에 저장된 암호화된 비밀번호를 비교하기 위해서는 checkpw 함수를 사용하면 됩니다. 아래와 같이 사용하며 이 함수도 마찬가지로 bytes 타입으로 값을 입력해야 하므로 encode를 하여 값을 넣어주어야 합니다.
bcrypt.checkpw( 'password'.encode('utf-8'), hashed_pw)
이렇게 비밀번호를 검증하여 일치하지 않으면 에러 메시지를 리턴하고, 일치하게 되면 성공 메시지와 함께 토큰을 발급하게 됩니다.
JWT 토큰에 대해서는 다음 블로그 내용으로 알아보도록 하겠습니다!!