[TIL] 인증, 인가

양희연·2020년 8월 13일
0

웹 개발 기초

목록 보기
6/6
post-thumbnail

💡 인증

user의 아이디와 비번을 확인하는 절차


> 로그인 절차

① user 아이디와 비번 생성
② user의 비번 암호화해서 DB에 저장
③ 아이디와 비번 입력 (로그인)
④ user가 입력한 비번 암호화 한후, 암호화되서 DB에 저장된 user 비번과 비교
⑤ 일치하면 로그인 성공
⑥ 로그인 성공하면 access token을 클라이언트에게 전송
    (access token에는 user 정보를 확인할 수 있는 정보가 있어야 함   a.k.a. user.id)

🔐 비밀번호 암호화

user의 비번은 절대 입력한 비밀번호 그대로 DB에 저장하지 않는다. ❗️꼭 암호화해서 저장해야 한다.❗️

비밀번호 암호에는 단방향 해시 함수가 일반적으로 쓰인다.

> 단방향 해시함수

원본 메시지를 변환하여 암호화된 메시지인 다이제스트를 생성한다.
원본 메시지를 알면 암호화된 메시지를 구하기는 쉽지만 암호화된 메시지로는 원본 메시지를 구할 수 없어서 단방향성이라고 한다.

but, 취약점이 있다. 해시 함수는 원래 짧은 시간에 데이터를 검색하기 위해 설계된 것이다.
그렇기 때문에 해시함수는 본래 처리 속도가 최대한 빠르도록 설계되었다. 이러한 속성 때문에 공격자는 매우 빠른 속도로 임의의 문자열의 다이제스트와 해킹할 대상의 다이제스트를 비교할 수 있다.

이 취약점을 보안하기 위해 일반적으로 2가지 보완점이 사용된다.

🔑 Bcrypt

비밀번호를 단방향 암호화하기 위해 만들어진 해시함수

salting
실제 비밀번호 이외에 추가적으로 랜덤 데이터를 더해서 해시값을 계산하는 방법

key stretching
단방향 해시값을 계산한 후 그 해시값을 또 또 해시하고, 이를 반복하는 것을 말한다.




💡 인가

사용자가 서버에 로그인 하면 해당 사용자가 맞는지 확인하는 과정


user가 로그인에 성공한 후부터는 access token 이라고 하는 암호화된 user 정보를 첨부해서 request 보내면, 서버에서 access token을 복호화해서 해당 user 정보를 얻게 된다.
이런 절차의 목적은 해당 user가 매번 로그인 하지 않게 하기 위함이다.

> 인가 절차

① 인증 절차를 통해 access token을 생성한다.
② user가 request를 보낼때 access token을 첨부해서 보낸다.
③ 서버에서 user가 보낸 access token을 복호화한다.
④ 복호화된 데이터를 통해 user의 id를 얻는다.
⑤ user id를 사용해서 DB에서 해당 user의 권한을 확인한다.
⑥ user가 충분한 권한을 가지고 있으면 해당 요청을 처리한다.
⑦ user가 권한을 가지고 있지 않으면 401 혹은 다른 에러 코드를 보낸다.

🔑 JWT (json web tokens)

access token을 생성하는 방법은 여러가지가 있는데, 그 중 가장 널리 사용되는 기술 중 하나가 JWT이다.

header
토큰의 타입과 해시 알고리즘 정보가 들어간다.

payload
만료시간을 나타내는 공개 클레임(조각), 클라이언트와 서버간 협의하에 사용하는 비공개 클레임

signature
base64로 인코딩된 header와 payload,
그리고 별도의 secret를 header에 지정된 알고리즘으로 암호화하여 전송되며, 복호화 가능하다.



🧑🏻‍🔬 예제

> hash

#가상환경 만들기
(base) $ conda create -n auth python=3.8
(base) $ conda activate auth

#bcrypt 설치
(auth) $ pip install bcrypt

#파이썬 콘솔 접속
(auth) $ python

#bcrypt import
>>> import bcrypt
>>> password = '1234'

#해싱하기 전에 encoding 먼저 해줘야 함
>>> encoded_password = password.encode('utf-8')

#bcrypt.hashpw(입력한 password encoding 값, bcrypt.gensalt())
>>> hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
>>> hashed_password
b'$2b$12$.y3klYC0dn9J4azBZ4.zN.vlTIPRphGo2Ebu8iPj1pFQnsvQZ4TaO'
>>> type(hashed_password)
<class 'bytes'>


#gensalt() 할 때마다 값이 바뀌는 것을 알 수 있다.
>>> bcrypt.gensalt()
b'$2b$12$a6IWweHPlNTe34LFiG0P7u'
>>> bcrypt.gensalt()
b'$2b$12$2dDwakBHIUR.3jPCJEW54u'
>>> bcrypt.gensalt()
b'$2b$12$OCK4KJTPdURnRH3QrVkNRO'

💡 DB에 저장할 때는 해시된 패스워드 값을 decode한 문자열로 저장하면 된다.
     field type을 charfield로 지정했을 경우

hashed_password.decode('utf-8')

> check

>>> bcrypt.checkpw(password.encode('utf-8'), hashed_password)
True

> jwt

#pyjwt 설치
(auth) $ pip install pyjwt

#파이썬 콘솔 접속
(auth) $ python

#jwt import
>>> import jwt

#jwt.encode(payload, key, algorithm)
#user의 id가 1인 값을 jwt 인코딩
>>> encoded_jwt = jwt.encode({'id' : 1}, 'secret', algorithm='HS256')
  • payload
    개인정보가 담겨 있으면 안된다.
    대신 user의 id 같은 경우는 단지 숫자라서 노출되도 괜찮다.

  • key
    시크릿 키로 유출되서는 안된다.
    실사용시 변수에 지정해서 사용할 것

>>> encoded_jwt
b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyLWlkIjo1fQ.tBQu0HfnOYK7lL3tH5ImgsI-y4Jz1RKscJWbV3U2QMI'
>>> type(encoded_jwt)
<class 'bytes'>

👉 생성된 토큰은 decode 후 프론트에게 JsonResponse로 전달
     encoded_jwt.decode('utf-8')

💡 전달된 토큰은 인증이 필요할 때 request의 headers의 Authorization으로 들어옴

request의 headers의 Authorization에서 받은 토큰을 jwt.decode 해서 payload를 풀어내 이용자 정보를 확인해 사용한다.

#decoding
>>> jwt.decode(encoded_jwt, 'secret', algorithm='HS256')
{'id' : 1}
profile
꾸준히 나아가자!

0개의 댓글