⛓인증/인가: 인증 Authentication❓❗️

허정원·2021년 3월 9일
0

인증 / 인가 는 Private API 는 물론이고 Public API 에서도 구현되어야 하는 기본적인 기능이다.
두편에 나눠서 다룰 예정이고, 이번엔 인증에 대해 포스팅 해보려고 한다.

인증 (Authentication) ??

인증이 무엇인가 하면 웹에 접속하려는 유저Identification확인하는 과정이다. 가장 대표적인 유저가 로그인을 시도 할 때, 우리 데이터베이스에 있는 유저의 정보와 일치하느냐 확인하는 것이다.

우선 로그인의 과정을 살펴보자,

  1. 유저가 회원가입을 통해 id 와 비밀번호를 입력한다.

  2. 입력한 비밀번호는 민감하고 유출되면 안되는 정보이기 때문에 암호화를 통해 데이터베이스에 입력된다.

  3. 회원가입을 성공적으로 마친 유저가 로그인을 시도 할 때 입력한 id 와 비밀번호를 데이터 베이스의 정보와 인증을 통해 로그인에 성공한다.

우리가 알고 있는 당연한 정보지만 여기서 중요한 것은 암호화 한 비밀번호를 어떻게 인증을 하는 것이다. 보통 암호화를 하는 방법은 단방향 해쉬 함수를 이용해 생성한 Digest 를 데이터베이스에 저장한다.

단방향 해쉬 함수란 (one-way hash function) ??

받은 문자열을 변환하여 digest생성할 순 있지만, 생성된 digest 를 원본 문자열만들 순 없다. 그래서 단반향이라고 한다.
때문에 인증을 위해선 받은 문자열같다면 매번 똑같은 digest생성한다.

여기까지 읽어보면 암호화한 비밀번호는 해킹에 아직도 취약하다. 이는 단방향 해쉬 함수가 암호화를 위해 나온 것이 아닌 컴퓨터가 문자열을 빠르게 읽고 검색하기 위해 사용되는 방법이여서 당연한 결과다.

이를 보완 가능한 것이 saltingkey-stretching 이다.

입력한 비빌번호 문자열에 임의의 문자를 salting 한다. 말 그대로 소금치듯이 더하는 것이다.
그 후 문자열을 해쉬 함수를 사용해 여러번 반복한다(key-stretching).

이제 문자열에서 암호화된 digest 를 얻기 위해선 salt 값과 key-stretching 한 횟수를 알아야 한다. 이제야 보안이 강화되었다.

문자열을 이리저리 볶는 과정을 직접 코딩하면 못할 건 아닌지만(지금은 못한다.) 귀찮기 때문에 유용하게 사용할 수 있는 라이브러리가 bcrypt 다. 우리가 생각한 과정을 다 해주는 착한 녀석이다.

import bcrypt

password = '1234'
encode_password = password.encode('utf-8')

해쉬 데이터를 얻기 위해선 문자열을 인코딩하여 데이터 타입을 bytes 로 바꿔줘야 한다.

...
digest = bcrypt.hashpw(encode_password, bcrypt.gensalt())
->b'$2b$12$11woQgqRdooz17wpl09ZLO7k2ExywqfUFPJYFfrjP1kwcF5El1MHK'

salting 을 하고 key-stretching 을 동시에 해줄 수 있다. 결과 값에서 맨 앞 b 는 데이터 타입이 bytes 라는 뜻이다.

이 정보를 데이터베이스에 바로 집어 넣으면 데이터 타입이 str이 되기 때문에 나중에 인증이 제대로 안되기 때문에 다시 디코딩을 통해 str 데이터 타입으로 변환 후 데이터베이스에 넣어 준다.

decode_digest = digest.decode('utf-8')
->'$2b$12$11woQgqRdooz17wpl09ZLO7k2ExywqfUFPJYFfrjP1kwcF5El1MHK'

이제 암호화 된 비밀번호를 데이터베이스에 안전하게 저장할 수 있다.

회원가입이 완료된 유저가 로그인을 시도하면 우리는 어떻게 인증을 해야 할까
받은 유저 id 를 데이터베이스에 있는지 확인하고 받은 비밀번호 문자열을 암호화 시켜 데이터베이스의 값과 비교하면 된다.

try:
    user = User.objects.get(user_id='요청으로 받은 유저 아이디 값')
    
except User.DoesNotExists:
    return JsonResponse({'message': 'INVALID USER_ID'}, status=401)

유저 id 가 존재한다면 객체를 가져오고 없다면 exception 처리를 한다.

객체를 얻었다면 id를 통해 얻어 온 객체의 비밀번호를 가져와 인코딩을 해주어 비교 할 수 있게 만들어 준다.

encode_password = user.password.encode('utf-8')

bcrypt.checkpw(data['password'].encode('utf-8'), encode_password)

True 값을 얻으면 로그인에 성공한다!!!

0개의 댓글