Spring security를 공부하다보면 JWT를 만나게 됩니다.
보안 인증을 JWT 토큰으로 지원할 수 있고 생각보다 높은 성능의 보안을 지원합니다.
https://docs.spring.io/spring-security/site/docs/current/reference/html5/
JWT의 구조는 Header, payload, signature로 이루어져있으며 각 항목의 구분자는 . 로 구분됩니다.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicGFzc3dvcmQiOiIxQTJzc2FBQSEhQCIsImlhdCI6MTUxNjIzOTAyMn0.K6Tib_A8OO_9bF7d6pnyvWNWuzI05EG-3lin-4M1-eQ
Header
Header는 알고리즘, type 으로 구성되어 있으며 key, value형태로 되어있습니다.
{
"alg": "HS256",
"typ": "JWT"
}
Json을 minify하고 base64로 hashing하면 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 값을 얻을 수 있습니다.
※Json을 minify하지 않으면 공백, 줄바꿈 문자들이 같이 encode 되기 때문에 원하는 값을 얻을 수 없습니다.
Payload
Payload 영역에는 JWT토큰에 담고싶은 내용을 적는 부분으로 보통 email. id 값을 작성하게 되며 exp(만료시간), iat(발행일자)값을 함께 동봉합니다.
{
"sub": "1234567890",
"name": "John Doe",
"password": "1A2ssaAA!!@",
"iat": 1516239022
}
payload를 encode하면 아래와 같은 값이 얻어집니다.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicGFzc3dvcmQiOiIxQTJzc2FBQSEhQCIsImlhdCI6MTUxNjIzOTAyMn0=
그런에 우리가 가지고 있는값인
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicGFzc3dvcmQiOiIxQTJzc2FBQSEhQCIsImlhdCI6MTUxNjIzOTAyMn0
와
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicGFzc3dvcmQiOiIxQTJzc2FBQSEhQCIsImlhdCI6MTUxNjIzOTAyMn0=
는 비교했을때 살짝 차이가 있습니다.
맨 뒤에 =이 사라지는 것인데 이것도 규칙이 있습니다.
간단하게는 URL에 safe 하지 않은 문자인 +,/,= 를 URL safe하게 변경해 주는 것 입니다.
base64 | base64url |
+ | - (minus) |
/ | _ (under line) |
= | none |
base64, base64 URL 자세히 보기
- base64 색인표
값 | 문자 | 값 | 문자 | 값 | 문자 | 값 | 문자 |
0 | A | 16 | Q | 32 | g | 48 | w |
1 | B | 17 | R | 33 | h | 49 | x |
2 | C | 18 | S | 34 | i | 50 | y |
3 | D | 19 | T | 35 | j | 51 | z |
4 | E | 20 | U | 36 | k | 52 | 0 |
5 | F | 21 | V | 37 | l | 53 | 1 |
6 | G | 22 | W | 38 | m | 54 | 2 |
7 | H | 23 | X | 39 | n | 55 | 3 |
8 | I | 24 | Y | 40 | o | 56 | 4 |
9 | J | 25 | Z | 41 | p | 57 | 5 |
10 | K | 26 | a | 42 | q | 58 | 6 |
11 | L | 27 | b | 43 | r | 59 | 7 |
12 | M | 28 | c | 44 | s | 60 | 8 |
13 | N | 29 | d | 45 | t | 61 | 9 |
14 | O | 30 | e | 46 | u | 62 | + |
15 | P | 31 | f | 47 | v | 63 | / |
- base64 url 색인표
값 | 문자 | 값 | 문자 | 값 | 문자 | 값 | 문자 |
0 | A | 16 | Q | 32 | g | 48 | w |
1 | B | 17 | R | 33 | h | 49 | x |
2 | C | 18 | S | 34 | i | 50 | y |
3 | D | 19 | T | 35 | j | 51 | z |
4 | E | 20 | U | 36 | k | 52 | 0 |
5 | F | 21 | V | 37 | l | 53 | 1 |
6 | G | 22 | W | 38 | m | 54 | 2 |
7 | H | 23 | X | 39 | n | 55 | 3 |
8 | I | 24 | Y | 40 | o | 56 | 4 |
9 | J | 25 | Z | 41 | p | 57 | 5 |
10 | K | 26 | a | 42 | q | 58 | 6 |
11 | L | 27 | b | 43 | r | 59 | 7 |
12 | M | 28 | c | 44 | s | 60 | 8 |
13 | N | 29 | d | 45 | t | 61 | 9 |
14 | O | 30 | e | 46 | u | 62 | - |
15 | P | 31 | f | 47 | v | 63 | _ |
위의 내용들을 하나하나 처리하고 encoding 한 결과를 보면 처음 생성했던 jwt 토큰과 하나하나 뜯어가며 만들어낸 jwt 토큰의 값은 아래와 같이 동일합니다.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicGFzc3dvcmQiOiIxQTJzc2FBQSEhQCIsImlhdCI6MTUxNjIzOTAyMn0
그런데 마지막 부분인 K6Tib_A8OO_9bF7d6pnyvWNWuzI05EG-3lin-4M1-eQ signature 부분은 아직 얻어내지 못했습니다.
이 signature 부분은 지금까지 encoding한 값인 algo + payload의 encoding 값과 user secret 값을 함께 sha256으로 한번 더 해시를 하면 얻을 수 있습니다.
signature부분은 알고리즘이나 payload의 작은 한부분만 변경되어도 값이 크게 달라지기 때문에 JWT가 보안성이 어느정도 좋다고 할 수 있는 이유입니다.
그러나 JWT를 잘못 이해해서 정확하게 사용하지 못한다면 오히려 보안이 약해질 수도 있습니다.
아래 사진은 JWT토큰을 decode한 내용입니다. 만약 사진처럼 payload영역에 password를 입력했다면 토큰만 서버에 요청해서 받은뒤 디코딩해서 비밀번호를 획득할 수 있게 됩니다.
적혀있는 JWT코드를 그대로 가져가서 decode하면 똑같은 결과를 얻을수 있을것입니다.
'IT 이야기 > Java' 카테고리의 다른 글
[JPA] UUID로 findBy 조회가 안되는 이유 (0) | 2021.08.15 |
---|---|
[JPA] Entity의 N:M 관계를 개발하면서 느낀 것 (0) | 2021.08.10 |
springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: JsonObject; (1) | 2021.08.08 |
[Bean] JAVA vs Spring (0) | 2021.07.27 |
JPA Default value 적용 - @DynamicInsert (0) | 2021.07.20 |
댓글