JWT(Json Web Token)-2021.12.02

Jonguk Kim·2021년 12월 2일
0

CS

목록 보기
4/9

1. JWT란?

  • 웹이나 모바일에서 사용자 인증을 위해서 사용하는 암호화 토큰을 말합니다.
  • Json 포맷을 이용하여 사용자에 대한 속성을 저장합니다.

2. JWT 토큰의 구성

header, payload, signature 3부분으로 구성된다. (점으로 구분)

  • header : 토큰의 타입, 해시 암호화 알고리즘

  • payload : 정보를 담는 공간(name : value 한 쌍으로 이루어짐)
    payload에 있는 속성들을 Claim Set이라고 부른다. (클라이언트와 서버 간의 주고받는 값들로 구성됨)

  • signature : 점을 구분자로 하여 header와 payload를 합친 문자열을 서명한 값
    header에 정의된 알고리즘 방식으로 SECRET_KEY를 이용하여 Base64 URL-Safe로 인코딩한다.

  • header, payload 값은 인코딩 될 뿐이지 따로 암호화 되지 않기 때문에 중요한 정보는 들어가면 쉽게 노출될 수 있다.
    하지만 signature의 경우 SECRET_KEY를 알지 못하면 복호화 할 수 없다.

  • 해쉬 암호화 방식을 사용하기 때문에 암호문을 평문으로 바꿀 수 없다. 또한 해쉬 암호화를 진행하게 되면 단 한 글자만 바꿔도 암호문이 완전히 달라진다. 따라서 만약 payload의 정보를 의도적으로 변경하여 요청을 한다 해도 검증과정에서 들통나게 되어있다.

  • 검증 방식은 받은 토큰의 header와 payload를 합친 문자열을 SECRET_KEY를 이용하여 암호화 한 값이랑 받은 토큰의 signature값이 같지 않으면 payload나 header의 정보가 바뀐것이므로 서버는 인가해주지 않는다.

3. JWT 인증 원리

  1. 사용자가 로그인을 하게 되면 서버로 로그인 정보(ID, PW)를 전송한다.

  2. 서버는 회원 정보가 담긴 DB를 조회하여 해당 User가 존재하는지 확인한다.

  3. 서버는 가입된 User임이 확인되면 "토큰"을 발급해준다.

  4. 사용자(클라이언트) 측에서는 받은 토큰을 저장해둔다.

  5. 데이터 요청 시, 서버한테 발급 받았던 토큰을 함께 보낸다.

  6. 서버는 토큰의 정보가 올바른지 검증을 시행한다.

  7. 검증이 잘 완료되면, 요청한 데이터를 보내준다.

=> 세션/쿠키 방식처럼 서버에 별도의 저장소가 필요없다!!

4. JWT 장단점

  • 장점

    • 별도의 저장소가 필요 없기 때문에 서버의 확장, 유지 보수가 용이하고 비용이 덜 든다.
    • 확장성이 뛰어나다. 토큰 기반으로 하는 다른 인증 시스템에 접근이 가능하다.
    • FaceBook이나 Google의 로그인은 모두 토큰을 기반으로 인증한다고 한다.
  • 단점

    • 한 번 발급하여 클라이언트에게 준 JWT에 대해서 돌이킬 수 없다. 서버 측에서 따로 저장해두지 않기 때문에 한 번 발급한 토큰은 유효 기간이 완료될 때 까지는 계속 사용 가능하다.
      => Access Token이 탈취당할 경우 보안에 취약하다.
    • 해결책으로 나온 방식은 기존의 Access Token의 유효기간을 매우 짧게 설정하고, 유효기간이 조금 긴 Refresh Token을 새로 발급한다. 따라서 Refresh Token은 Access Token의 유효기간이 만료됬을 때 새로 또 발급해주는 열쇠가 된다!
    • Payload의 정보가 제한적이다. payload는 따로 암호화 되지 않기 때문에 중요한 정보들을 넣을 수는 없다.
    • JWT 길이가 길다. 따라서 인증이 필요한 요청이 많아질수록 서버의 자원 낭비가 발생한다.

5. JWT 구현 예제

5-1. 토큰 발급 과정

# 로그인 성공할때 토큰 생성
@app.route('/api/login', methods=['POST'])
def sign_in():
    id_receive = request.form['username_give'] #사용자가 입력한 ID
    pw_receive = request.form['password_give'] #사용자가 입력한 PW

    result = db.userdb.find_one({'id': id_receive, 'pw': pw_receive}) #DB에서 사용자 찾기

    if result is not None: #사용자가 존재한다면 토큰 발급
        payload = { #JWT payload에 들어갈 부분을 name / value 형식으로 지정해주기
            'id': id_receive, #사용자 ID를 payload에 담기
            'exp': datetime.utcnow() + timedelta(seconds=60 * 60 * 24)  # 유효기간 설정, 24시간으로 설정함.
        }

        token = jwt.encode(payload, SECRET_KEY, algorithm='HS256') # HASH-256 방식으로 암호화, 토큰 발급
        
        return jsonify({'result': 'success', 'token': token}) # 사용자에게 토큰 전달
    else: #사용자가 존재하지 않는다면 에러 메세지 응답
        return jsonify({'result': 'fail', 'msg': '아이디/비밀번호가 일치하지 않습니다.'})

paylod - 이미 등록된 Claim 종류

  • 등록된 클레임이란 토큰 정보를 표현하기 위해 이미 정해진 종류의 데이터들로, 선택적으로 작성이 가능하다.
    위의 예제에서는 exp만 사용했다.
    JWT 길이를 간결하게 하기 위해 key는 모두 길이가 3이다.
    sub는 보통 유니크한 값을 적용한다.
    • iss: 토큰 발급자(issuer)
    • sub: 토큰 제목(subject)
    • aud: 토큰 대상자(audience)
    • exp: 토큰 만료 시간(expiration), NumericDate 형식으로 되어 있어야 함 ex) 1480849147370
    • nbf: 토큰 활성 날짜(not before), 이 날이 지나기 전의 토큰은 활성화되지 않음
    • nbf: 토큰 활성 날짜(not before), 이 날이 지나기 전의 토큰은 활성화되지 않음
    • jti: JWT 토큰 식별자(JWT ID), 중복 방지를 위해 사용하며, 일회용 토큰(Access Token) 등에 사용

5-2. 토큰 검증 과정

  • 사용자에게서 받은 토큰을 검증하여 검증이 된다면 payload에서 사용자의 ID를 DB에 조회한다.
    DB상에서 사용자 정보가 존재한다면, 회원이 맞으므로 요청한 데이터를 응답 메세지로 보냄
@app.route('/')
def home():
    token_receive = request.cookies.get('mytoken') #사용자에게서 토큰을 받음

    try:
        payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256']) #받은 토큰 검증
        user_info = db.userdb.find_one({'id': payload['id']})
        if user_info is not None:
            postdata = list(db.cafelist.find({}, {'_id': False}))

        return render_template("layout_postlist.html", postdata=postdata) #요청 데이터 응답

    except jwt.ExpiredSignatureError: # 유효기간 만료 시 응답
        return redirect(url_for("login", msg="로그인 시간이 만료되었습니다."))
    except jwt.exceptions.DecodeError: # 페이로드 값이 다를 때의 응답
        return redirect(url_for("login", msg="로그인 정보가 존재하지 않습니다."))
profile
Just Do It

0개의 댓글