Session - Backend Auth

Sung Jun Jin·2020년 4월 14일
0

위코드 세션 정리

목록 보기
8/12

인증 (Authentication)

  • 유저의 identification을 확인하는 절차이다. 즉, 유저의 아이디와 비밀번호를 확인하는 절차이다.
  • 인증을 하기 위해서는 유저의 아이디와 비밀번호를 생성할 수 있는 기능도 필요하다

일반적인 유저 로그인 절차는 다음과 같다

  1. 유저 아이디 비번 생성
  2. 유저 비번을 암호화해서 DB에 저장
  3. 유저 로그인 -> 아이디와 비밀번호 입력
  4. 유저가 입력한 비밀번호를 암호화 한 후, DB에 저장된 유저 비밀번호와 비교
  5. 일치하면 로그인 성공, access token을 클라이언트에 전송
  6. access token을 헤더에 첨부해 request 서버에 전송하면서 로그인 상태 유지

Bcrypt

유저의 비밀번호를 암호화 하기 위해 사용하는 기술은 일반적으로 단방향 해쉬 함수(one-way hash function)이다. 여기에 기존 단방향 해쉬 함수의 취약점을 보완하기 위해 Salting과 Key Streching 기법이 들어간다.

Salting : 실제 비밀번호에 더해지는 추가적인 랜덤 데이터. 해쉬 값을 무작위로 만들어준다.
Key Stretching : 단방향 해쉬값을 계산 한 후 그 해쉬값을 계속 반복해 hashing 하는 것을 의미한다.

Salting과 Key Stretching을 구현한 해쉬 함수중 가장 보편적으로 사용되는 것이 bcrypt이다. bcrypt는 처음부터 비밀번호를 단방향 암호화 하기 위해 만들어진 해쉬함수이다.

bcrypt 설치 및 사용법

1. 설치

$ pip install bcrypt

Collecting bcrypt
  Using cached bcrypt-3.1.7-cp34-abi3-manylinux1_x86_64.whl (56 kB)
Collecting six>=1.4.1
  Using cached six-1.14.0-py2.py3-none-any.whl (10 kB)
Collecting cffi>=1.1
  Using cached cffi-1.14.0-cp38-cp38-manylinux1_x86_64.whl (409 kB)
Collecting pycparser
  Using cached pycparser-2.20-py2.py3-none-any.whl (112 kB)
Installing collected packages: six, pycparser, cffi, bcrypt
Successfully installed bcrypt-3.1.7 cffi-1.14.0 pycparser-2.20 six-1.14.0

2. bcrypt를 통한 암호화

  1. bcrypt import
import bcrypt
  1. '1234'라는 평문 패스워드를 bcrypt를 통해 암호화 시켜보자. 암호화를 하기 위해서 우선 str 타입인 변수를 byte 타입으로 변환시켜줘야 한다. .encode() 혹은 bytes() 함수를 사용해준다
password = '1234'

bytes(password,'utf-8') # 방법 1
password = password.encode('utf-8')# 방법 2
  1. bcrypt.haspw() 메소드를 사용해 해싱해준다. bcrypt.hashpw()에 들어가는 인자는 다음과 같다
    bcrypt.hashpw(비밀번호, Salt)
    Salt는 bcrypt.gensalt() 메소드를 사용하면 자동적으로 생성된다.
hashed_password = bcrypt.hashpw(password, bcrypt.gensalt())
print(hashed_password) 
# 출력 : b'$2b$12$z7dSHnEpqUovbFqUB1OeXesMiKaV5O/CtgFnfRhaWX3Wh6bbXRh6O'
  1. 해쉬가 완료된 비밀번호는 byte 타입이기 때문에 다시 decoding 작업을 통해 형변환을 시켜준 후 DB에 저장한다.
hashed_password = hashed_password.decode('utf-8')
print(hashed_password)
# 출력 : $2b$12$GhvfLU5XFhhedwygHmwI.eULRrHkFG8Di.n6IQxs16pTidZ0yFuVW

2. 암호화된 DB와 유저가 입력한 비밀번호를 암호화 한 후 비교하기

  1. 로그인 하려고 하는 사용자가 '1234'라는 비밀번호를 입력했고, 입력한 비밀번호를 user_password라는 변수에 저장했다.
user_password = '1234'
  1. 똑같이 이진 데이터 (bytes)로 encoding 해준다.
user_password = user_password.encode('utf-8')
  1. bcrypt.checkpw 메소드를 사용해 기존에 저장되어있던 암호화된 비밀번호와 비교해준다. 일치하면 True, 아니면 False를 반환한다. 물론 DB에서 가져온 암호화된 데이터는 다시 bytes로 encoding 해줘야 한다. brcypt.checkpw()에 들어가는 인자는 다음과 같다
    bcrypt.checkpw(사용자가 입력한 비밀번호, 기존 DB에서 불러온 암호화된 비밀번호)
result = bcrypt.checkpw(user_password,hashed_password)
print(result) # 출력 : True

인가 (Authorization)

  • Authorizaiton은 유저가 요청하는 request를 실행할 수 있는 권한이 있는 유저인가를 확인하는 절차이다 ex) 해당 유저는 고객 정보를 볼 수 있지만 수정은 불가능
  • JWT(JSON Web Token)을 사용해 구현할 수 있다

일반적인 유저 Authorization 절차는 다음과 같다

  1. Authentication 절차를 통해 access token을 생성한다. access token에는 유저 정보를 확인할 수 있는 정보가 들어가야 한다
  2. 유저가 request를 보낼때 access token을 첨부해서 보낸다
  3. 서버에서는 유저가 보낸 access token을 복호화 한다
  4. 복호화된 데이터를 통해 user_id를 얻는다
  5. user_id를 통해 DB에서 해당 유저의 권한을 확인하고 권한을 가지고 있으면 요청을 처리한다
  6. 유저가 권한을 가지고 있지 않으면 401(Unauthorized Response) 혹은 다른 에러 코드를 return한다.

JWT

JWT는 access token을 생성하는 가장 널리 사용되는 기술이다. 말 그대로 사용자의 정보를 담은 JSON 데이터를 암호화해서 클라이언트와 서버간에 주고 받는 것 이다

PyJWT 설치 및 사용법

1. PyJWT 설치

$ pip install PyJWT 
Collecting PyJWT
Using cached PyJWT-1.7.1-py2.py3-none-any.whl (18 kB)
Installing collected packages: PyJWT
Successfully installed PyJWT-1.7.1

2.PyJWT를 통한 access token 발급
1. import jwt : 패키지명은 pyjwt이지만 import할때 이름은 jwt이다

import jwt
  1. 랜덤한 조합의 key를 생성한다. 예제이므로 단순하게 'secret-key'라고 해준다. 2번째 인자로 들어갈 payload을 작성해준다. payload에는 exp와 같이 만료시간을 나타내는 공개 클레임, 클라이언트와 서버간 협의하에 사용하는 비공개 클레임이 들어간다.
SECRET = 'secred-key'
payload = {'user_id' : 1}
  1. jwt.encode() 메소드를 통해 access_token을 생성한다. 들어가는 인자는 다음과 같다
    jwt.encode(payload,key,algorithm)
    생성되는 access_token은 byte형식이므로 decode 작업을 통해 리턴된다.
access_token = jwt.encode(payload,SECRET,algorithm='HS256'
access_token = access_token.decode('utf-8')
print(access_token)
# 출력 : eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxfQ.QjEN7esJq0M_X9TuSjMePV-_nvaI58z6nX5lew2rX0c

3.넘겨받은 access_token 검증하기

  1. jwt_decode() 메소드를 통해 넘겨받은 access_token이 발행한 토큰이 맞는지 확인한다. 인자는 다음과 같다
    jwt.decode(jwt,key,algorithm)
access_token = access_token.encode('utf-8') # bytes로 encoding
header = jwt.decode(access_token,SECRET,algorithm = 'HS256')
print(header) # 출력 : {'user_id': 1}

사용자의 정보가 담긴 payload가 출력되었다. payload로 기존 DB와 대조해 사용자가 존재하는지 확인하고 사용자의 권한을 확인한다.

인가는 자주 사용하기 때문에 utils.py(권장), apps.py에 decorator로 따로 구현해준다

profile
주니어 개발쟈🤦‍♂️

0개의 댓글