
시작하며
JWT 토큰이란?
- JWT 토큰은 Json Web Token의 약자임.
- 웹 인증 토큰이나 세션, REST API에서 많이 활용되는 토큰의 방식임.
- 사용자 인증에 필요한 정보를 암호화된 형태로 클라이언트에 저장함.
JWT 토큰의 구성
- "헤더"와 "페이로드", "서명" 부분으로 구성되어 있음.
- "헤더"와 "페이로드"는 복호화 가능한 부분들임. 헤더와 페이로드는 Base64 방식으로 인코딩되어 전송되고,
쉽게 디코딩하여 원래의 JSON 형태로 볼 수 있음. - 반면 "헤더"와 "페이로드"를 제외하고 남은 "서명" 부분은 복호화할 수 없음.
- "서명" 부분은 말 그대로 헤더와 페이로드가 변조되지 않았는지, 문제가 없는 데이터인지 확인하는 Signature 역할을 함.
JWT 토큰의 암호화 그리고 무결성
JSON으로 담은 "헤더"와 "페이로드" 데이터를 Base64 암호화 기법으로 암호화 하고, SHA256이나 RSA 암호화 기법을 이용하여 "서명"부분을 단방향 암호화함으로써 그 무결성을 검증하게 됨.
이 포스트는 JWT를 통한 사용자 인증 프로세스에 초점을 맞추고 있음.
사용자 인증과 그 무결성 검증은 웹 애플리케이션의 보안에서 필수적인 부분임.
JWT 인증 프로세스의 구현
JWT 인증 프로세스를 구현하는 방법을 설명해 보겠음.
const jwt = require('jsonwebtoken');
const secretKey = 'your_secret_key';
const sql = `SELECT * FROM users WHERE username = '${uname}'`;
// .. MySQL Query Process ...
let user = await db.query(sql);
if (!user) {
// 사용자 로그인 실패. 사유: 존재하는 username 없음
res.json({"error":true, "msg": "해당하는 유저가 없음."});
} else {
// 로그인 프로세스 1차 성공. username 존재
const hashedPassword = md5(password);
const rowPassword = user.password;
if (hashedPassword != rowPassword) {
// 사용자 로그인 실패. 사유: username은 존재하지만 비밀번호 불일치
res.json({"error":true, "msg": "해당하는 유저가 없음."});
} else {
// 로그인 최종 성공. JWT에 정보를 저장한다
const userRow = {
id: user.id,
username: user.username,
permission: user.permission
};
const token = jwt.sign(userRow, secretKey, { expiresIn: '1h' });
// Return
res.json({"error":false, "msg": "로그인 성공함.", "token": token});
}
}
- 사용자가 로그인을 시도하면, 서버는 사용자 정보를 검증함.
- 정보가 정확하면 서버에서 JWT를 생성하고 이를 사용자에게 전송함.
- 사용자는 이후의 요청에 이 JWT를 포함시켜 보냄.
예를 들면 헤더에 Authorization: Bearer ${token} 을 포함한채로 전송하게 됨. - 서버는 요청을 받을 때마다 미들웨어를 통해 JWT를 검증하여 사용자가 유효한지 확인함.
JWT 생성시에는 jwt.sign() 함수를 사용하였지만,
복호화하여 데이터의 무결성을 검사할시에는 jwt.verify() 를 이용하여 검사함.
장단점 정리
JWT를 사용하며 오는 장점
- JWT의 장점은 상태를 서버가 아닌 클라이언트에 저장함.
따라서 세션이 저장소나 메모리를 차지하지 않아 대규모 접속 발생시에도 퍼포먼스에 문제가 없음 - 복호화가 가능한 데이터와 단방향암호화 서명을 가지고 있어 데이터 무결성 검증에 유리함,
손쉽게 데이터를 복호화해서 꺼내 쓸 수 있음
JWT를 사용하며 오는 단점
- 유저 (클라이언트) 측에 토큰이 직접 노출(보관) 되기때문에,
위 코드블럭에서 작성한 secretKey와 같은 부분이 유출될 경우 권한 탈취 및 해킹의 위험이 있음. - 유저를 통해 인증된 토큰이 탈취되면, 탈취자는 토큰이 만료될 때까지 사용자로서 시스템에 접근할 수 있음. (XSS 공격)
- 일단 발행된 토큰은 서버에서 만료를 강제할 수 없어 (iat과 expiresat이 지정되어있음), 블랙리스팅 시스템을 따로 구현해야 함.
JWT가 사용되는 대표적인 사례
모든 사례를 일일히 정리할 수는 없지만, 가장 대표적으로 오는 사례들을 간단히 정리해본다.
- 단일 페이지 애플리케이션(SPA): 클라이언트 사이드에서 사용자 세션을 관리하는 데 사용됨.
- 모바일 애플리케이션: 서버와 모바일 앱 간의 안전한 통신을 위해 사용됨.
- 마이크로서비스 아키텍처: 서비스 간의 사용자 인증 정보 전달에 사용됨.
- 사용자 인증 및 권한 부여: 사용자의 인증 정보와 권한을 포함하여 API 서버에 전달하는 데 사용됨.
- 소셜 로그인 구현: 소셜 미디어 계정을 통한 로그인 시 사용자 정보 교환에 사용됨.
마치며
기존에 JWT는 내가 주로 사용해오던 인증 토큰 시스템이다.
이번 포스트를 통해 JWT를 사용하면서 발생할 수 있는 약점과 그 외 단점들을 알게되어 더욱 신중하게 이를 피해 코딩할 수 있을것 같다.
Jan 19, 2024 Copyright deVlog. All rights reserved.
2024년 01월 19일 deVlog 작성, 모든 권리 보유