1. JWT란?
- JSON Web Token의 줄임말로 인증에 필요한 정보들을 암호화시킨 토큰을 뜻합니다.
- JSON 객체를 사용해 정보를 안정성 있게 전달하는 웹표준입니다.
- 사용자는 Access Token(JWT 토큰)을 HTTP 헤더에 실어 서버로 보내게 됩니다.
- 예를 들어, 로그인 기능을 생각해보면 사용자가 로그인하면 서버에서 회원임을 인증하는 토큰을 넘겨줌으로써 이후 회원만 접근할 수 있는 서비스 영역에서 신분을 확인하는 데 쓰일 수 있습니다.
2. 사용법
토큰을 만들기 위해서는 크게 3가지, Header,Payload, Verify Signature가 필요합니다.
Header : 위 3가지 정보를 암호화할 방식(alg), 타입(type) 등이 들어갑니다.
Payload : 서버에서 보낼 데이터가 들어갑니다. 일반적으로 유저의 고유 ID값, 유효기간이 들어갑니다.
- Payload는 정보 하나하나를 각각 클레임 (claim)이라고 부른다.
- Claim은 key/value 로 작성해야한다.
- Claim도 registered claim, public claim, private claim으로 나눈다.
Verify Signature : Base64 방식으로 인코딩한 Header,payload 그리고 SECRET KEY를 더한 후 서명됩니다.
$.ajax({
type: "POST",
url: "/sign_in",
data: {
role_give: role,
userid_give: userid,
password_give: password
},
success: function (response) {
if (response['result'] == 'success') {
$.cookie('mytoken', response['token'], {path: '/'});
{#window.location.replace("/")#}
window.location.href='/'
} else {
alert(response['msg'])
}
}
});
@application.route('/sign_in', methods=['POST'])
def sign_in():
# 로그인
role_receive = request.form['role_give']
userid_receive = request.form['userid_give']
password_receive = request.form['password_give']
pw_hash = hashlib.sha256(password_receive.encode('utf-8')).hexdigest()
result = db.users.find_one({'role': role_receive, 'userid': userid_receive, 'password': pw_hash})
if result is not None:
payload = {
'id': userid_receive,
'exp': datetime.utcnow() + timedelta(seconds=60 * 60 * 24) # 로그인 24시간 유지
}
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
return jsonify({'result': 'success', 'token': token})
# 찾지 못하면
else:
return jsonify({'result': 'fail', 'msg': '아이디/비밀번호가 일치하지 않습니다.'})
최종적인 결과 : Encoded Header + "." + Encoded Payload + "." + Verify Signature
Header, Payload는 인코딩될 뿐(16진수로 변경), 따로 암호화되지 않습니다. 따라서 JWT 토큰에서 Header, Payload는 누구나 디코딩하여 확인할 수 있습니다. 여기서 누구나 디코딩할 수 있다는 말은 Payload에는 유저의 중요한 정보(비밀번호)가 들어가면 쉽게 노출될 수 있다는 말이 됩니다.
하지만 Verify Signature는 SECRET KEY를 알지 못하면 복호화할 수 없습니다.
def getUserInfoByToken():
token_kakao = request.cookies.get('kakao')
token_receive = request.cookies.get('mytoken')
user_info = ''
if token_receive is not None:
try:
payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
user_info = db.users.find_one({"userid": payload["id"]})
except jwt.ExpiredSignatureError:
return redirect(url_for("login", msg="로그인 시간이 만료되었습니다."))
except jwt.exceptions.DecodeError:
return redirect(url_for("login", msg="로그인 정보가 존재하지 않습니다."))
if user_info is not None:
return user_info;
if user_info is None:
user_info = 'a';
return user_info;
3. 장&단점
3-1) 장점
- 세션/쿠키는 별도의 저장소의 관리가 필요합니다. 그러나 JWT는 발급한 후 검증만 하면 되기 때문에 추가 저장소가 필요 없습니다. 이는 Stateless 한 서버를 만드는 입장에서는 큰 강점입니다.
- Stateless는 어떠한 별도의 저장소도 사용하지 않는, 즉 상태를 저장하지 않는 것을 의미합니다. 이는 서버를 확장하거나 유지,보수하는데 유리합니다.
- 토큰 기반으로 하는 다른 인증 시스템에 접근이 가능합니다.(높은 확장성)
- 예를 들어 Facebook 로그인, Google 로그인 등은 모두 토큰을 기반으로 인증을 합니다. 이에 선택적으로 이름이나 이메일 등을 받을 수 있는 권한도 받을 수 있습니다.
3-2) 단점
- 이미 발급된 JWT에 대해서는 돌이킬 수 없습니다. 세션/쿠키의 경우 만일 쿠키가 악의적으로 이용된다면, 해당하는 세션을 지워버리면 됩니다. 하지만 JWT는 한 번 발급되면 유효기간이 완료될 때 까지는 계속 사용이 가능합니다. 따라서 악의적인 사용자는 유효기간이 지나기 전까지 신나게 정보들을 털어갈 수 있습니다.
- SECRET KEY가 없으면 복호화가 안된다면서 왜 털려? 공격자가 매우 스마트하면 털리겠지
- 해결책 => 기존의 Access Token의 유효기간을 짧게 하고 Refresh Token이라는 새로운 토큰을 발급합니다. 그렇게 되면 Access Token을 탈취당해도 상대적으로 피해를 줄일 수 있습니다.
- Payload 정보가 제한적입니다. 위에서 언급했다시피 Payload는 따로 암호화되지 않기 때문에 디코딩하면 누구나 정보를 확인할 수 있습니다. (세션/쿠키 방식에서는 유저의 정보가 전부 서버의 저장소에 안전하게 보관됩니다) 따라서 유저의 중요한 정보들은 Payload에 넣을 수 없습니다.
- 세션/쿠키 방식에 비해 JWT의 길이는 깁니다. 따라서 인증이 필요한 요청이 많아질 수록 서버의 자원낭비가 발생하게 됩니다.
4. 세션/쿠키 인증방법
서버에서 받은 Session ID를 쿠키에 저장하여 사용
2022.05.30 - [프로그래밍/개발지식] - 쿠키 & 세션
참조 블로그 : 그랩의 블로그
쉽게 알아보는 서버 인증 1편(세션/쿠키 , JWT) : https://tansfil.tistory.com/58?category=255594
'프로그래밍 > 개발지식' 카테고리의 다른 글
정처기 시험대비 메모장 2 - 22년 1회 필기 (0) | 2022.05.04 |
---|---|
정처기 시험대비 메모장 1 (0) | 2022.05.03 |
해시함수란? (0) | 2022.05.03 |
API 키란? (0) | 2022.04.29 |
정적 웹페이지 vs. 동적 웹페이지 (0) | 2022.04.29 |