JWT(JSON Web Token)는 로그인 및 인증 시스템에서 널리 사용되는 토큰 기반 인증 방식이다. 로그인 후 서버가 클라이언트에게 JWT를 발급하며, 이후 클라이언트는 해당 JWT를 사용하여 인증된 요청을 서버에 보낸다.
JWT는 크게 Header, Payload, Signature 세 부분으로 구성된다.
plaintext
복사편집
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJ1c2VyX2lkIjoxMjMsIm5hbWUiOiJKb2huIERvZSJ9
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
user_id, role 등)를 포함사용자가 로그인 요청을 보냄
서버가 사용자의 아이디/비밀번호를 확인
JWT를 생성하여 클라이언트에게 반환
SECRET_KEY로 서명되어 위변조가 불가능함클라이언트가 JWT를 쿠키에 저장
JWT를 저장하는 방법에는 여러 가지가 있지만, 쿠키(cookie) 를 사용하는 것이 보안적으로 유리하다.
자동 전송
보안 강화 (HttpOnly, Secure 옵션 사용 가능)
HttpOnly: JavaScript에서 쿠키에 접근할 수 없도록 막음 (XSS 공격 방어)Secure: HTTPS에서만 쿠키를 전송하도록 제한하여 중간자 공격(Man-in-the-Middle Attack)을 방지CSRF 방지
SameSite 옵션을 설정하여 CSRF(사이트 간 요청 위조) 공격을 예방할 수 있음브라우저 세션 유지 가능
XSS(크로스 사이트 스크립팅) 공격 방지
HttpOnly=True 옵션을 설정하면 JavaScript에서 쿠키 접근이 불가능하여 공격자가 토큰을 탈취하는 것을 방지할 수 있음CSRF(사이트 간 요청 위조) 공격 방지
SameSite="Lax" 또는 "Strict" 설정을 하면 CSRF 공격을 차단할 수 있음SameSite="Strict"은 가장 보안이 강하지만, 다른 사이트에서 쿠키를 전송하지 않기 때문에 일부 기능(소셜 로그인 등)이 제한될 수 있음SameSite="Lax"는 보안과 편의성의 균형을 맞춘 설정JWT의 만료 시간 고려
로그아웃 처리
이렇게 설정하면 보안성이 높은 JWT 기반 로그인 시스템을 구축할 수 있다!
python
복사편집
from flask import Flask, request, jsonify, make_response
import jwt
import datetime
app = Flask(__name__)
SECRET_KEY = "mysecretkey" # JWT 서명에 사용할 키
# 로그인 API (JWT 생성 및 쿠키 저장)
@app.route('/login', methods=['POST'])
def login():
data = request.json # 사용자 입력 데이터
username = data.get("username")
password = data.get("password")
if username == "admin" and password == "password": # 예제용 인증
token = jwt.encode(
{"user": username, "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1)},
SECRET_KEY, algorithm="HS256"
)
response = make_response(jsonify({"message": "로그인 성공"}))
response.set_cookie("token", token, httponly=True, secure=True) # JWT를 쿠키에 저장
return response
return jsonify({"message": "로그인 실패"}), 401
# 인증 확인 API (쿠키에서 JWT 확인)
@app.route('/protected', methods=['GET'])
def protected():
token = request.cookies.get("token") # 쿠키에서 JWT 가져오기
if not token:
return jsonify({"message": "토큰이 없습니다!"}), 403
try:
data = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
return jsonify({"message": f"환영합니다, {data['user']}!"})
except jwt.ExpiredSignatureError:
return jsonify({"message": "토큰이 만료되었습니다!"}), 403
except jwt.InvalidTokenError:
return jsonify({"message": "유효하지 않은 토큰입니다!"}), 403
if __name__ == "__main__":
app.run(debug=True)