복습 必 어언 4년전... 나의 기억력은 1년만 영어를 사용하지 않아도 사라지는 나의 단어 기억력 인간의 메모리란 모델에서 벡터만 뽑아서 저장해서 가볍게 만드는 추상화 형태인가봄 형태는 기억하니까
- 물론 인간의 기억력을 믿지 않음 그 과정에서 인간은 본인의 감정과 개인적인 사고, 상상도 분리할 줄 모르고 그걸 상상과 본인의 주관적인 인식을 반영해서 저장함 오류 천지임
- 객관성과 본인의 기억이 맞다고 전제하는 인간은 상당히 위험한 인간일 확률이 높음 메타인지나 인간에 대한 이해가 낮을 확률이 높음
- 메모장이나 기록을 이길 기억력은 없잖아 아 있다 조작된 기록 혹은 서사가 있고 알리바이나 구조가 정확하면 채택 가능성 있겠다 혹은 현장 증거 근데 증거가 기록이지 뭐 기록이 없을 때만 가치를 발현하겠다
- 증거가 눈 앞에 있는데 부정하는 것은 이제 정신착란이나 자기 부정이나 회피 aka.우기기 or aka.개소리 혹은 누군가가 의도적으로 1시간 짜리 동영상 5분 만 잘라서 내러티브를 조작하는 것일 수도 있고 aka.악마의 편집 or aka.맥락의 부재
JWT 토큰 방식은 웹표준(RFC 7519)로서 두 개체에서 JSON 객체를 사용하여 가볍고 자가수용적인(self-contained) 방식으로 정보를 안정성 있게 전달한다.
자가수용적이라는 의미는 JWT 안에 인증에 필요한 모든 정보를 자체적으로 지니고 있다는 의미이다.

사용자는 클라이언트에서 ID/PW를 통해 로그인을 요청한다.
유효한 ID/PW라면, Access token & Refresh token을 발급한다.
클라이언트는 전달 받은 토큰들은 localStorage에 저장한다.
클라이언트는 헤더에 Access token을 담아 서버에 요청한다.
서버에서는 Access token을 검증하고, 응답을 클라이언트로 보낸다.
Access token이 유효하지 않다면 Refersh token으로 Access token을 재발급한 뒤, access token을 리턴해준다.

. 구분자가 들어간다.
헤더는 두 가지 정보를 가진다.
typ: 토큰의 타입(JWT)
alg: 해싱 알고리즘
클레임은 키 값 형태로 존재한다.
등록된(registered) 클레임
등록된 클레임들은 서비스에서 필요한 정보들이 아닌, 토큰에 대한 정보들을 담기 위하여 이름이 이미 정해진 클레임들이다.
등록된 클레임의 사용은 모두 선택적(optional)이며, 이에 포함된 클레임 이름들은 다음과 같다.
iss: 토큰 발급자 (issuer)
sub: 토큰 제목 (subject)
aud: 토큰 대상자 (audience)
exp: 토큰의 만료시간 (expiraton)
시간은 NumericDate 형식으로 되어있어야 한다. (예: 1480849147370)
현재 시간보다 이후로 설정되어 있어야합니다.
nbf: Not Before 를 의미하며, 토큰의 활성 날짜와 비슷한 개념입니다.
iat: 토큰이 발급된 시간 (issued at)
jti: JWT의 고유 식별자
공개(public) 클레임
공개 클레임들은 충돌이 방지된 (collision-resistant) 이름을 가지고 있어야 한다.
충돌을 방지하기 위해서는, 클레임 이름을 URI 형식으로 짓는다.
{
"https://velopert.com/jwt_claims/is_admin": true
}
비공개(private) 클레임
등록된 클레임도 아니고, 공개된 클레임도 아니다. 양측 간에(클라이언트 - 서버) 협의 하에 사용되는 클레임 이름들이다.
공개 클레임과는 달리, 이름이 중복되어 충돌이 될 수 있으니 사용할 때 유의해야 한다.
{
"username": "velopert"
"email": "velpert@nnn.com"
}
예제 Payload
{
"iss": "velopert.com",
"exp": "1485270000000",
"https://velopert.com/jwt_claims/is_admin": true,
"userId": "11028373727102",
"username": "velopert"
}
Signature 이란 토큰을 인코딩하거나 유효성 검증을 할 때 사용하는 고유한 암호화 코드이다.
서명 생성 과정
헤더와 페이로드 값을 각각 BASE64 로 인코딩
위에서 인코딩한 값을 비밀 키를 이용해 헤더에서 정의한 알고리즘으로 해싱
위에서 해싱한 값을 다시 BASE64 로 인코딩
인증에 필요한 정보가 토큰에 있기에 별도의 저장소가 필요없다.
Cookie와 Session 사용 시 문제점이였던 stateful 한 특성을 JWT 토큰 사용 시에는 stateless 하게 가져갈 수 있다.
HTTP 헤더에 넣어서 쉽게 전달 가능하다.
확장성에 용이하다. MSA 환경에 적용하기 편리하다.
거의 모든 요청에 토큰이 포함되므로 트래픽 크기에 영향을 미칠 수 있다.
토큰에 정보가 많아져 토큰의 크기가 커지면 네트워크에 부하를 줄 수 있다.
페이로드는 암호화된 게 아니라 BASE64 로 인코딩된 것이므로, 중간에 토큰을 탈취하면 페이로드의 데이터를 모두 볼 수 있다.
JWT 토큰 생성 시, JWT 헤더와 페이로드 정보를 인코딩하고, 둘을 합친 문자열을 비밀 키로 서명한다.
암호화, 복호화 키가 같으면 대칭키 암호화 방식이라고 한다.
대표적으로 HMAC 암호화 알고리즘이 있다.
HS256, HS384, HS512 .... 가 이에 해당하고,
뒤 숫자는 secret key의 최소 바이트 크기를 의미한다.
기본적으로 단방향 암호화 알고리즘인 SHA-256 과 함께 쓰인다.
값에 SHA-256를 적용해서 해싱 후 private key( == secret key , 대칭키 역할)로 암호화 한다.
private key를 알고있는 서버만 Signature 유효성 검증이 가능하다. 즉 JWT를 복호화 할 수 있다.
암호화, 복호화 키가 다르면 비대칭키 암호화 방식이라고 한다.
다른 키를 사용해 암호화, 복호화를 수행하기 때문에 속도가 느리지만, 대칭키 암호화에 비해 안전하다.
대표적으로 RSA 암호화 알고리즘이 있다.
값에 SHA-256 을 적용해서 해싱 후 비밀키(private key)로 암호화한다.
대칭키 암호화 방식 같은 경우, private key를 모르는 서버는 JWT의 유효성을 검증할 수 없다.
반대로 비대칭키 암호화 방식은 private key를 몰라도 public key를 통해 복화할 수 있기 때문에 JWT의 유효성을 검증할 수 있다.
대칭키 암호화 방식에서의 인증서버 구축하기
흐름을 살펴보자.
인증 서버가 클라이언트에게 JWT를 발급
클라이언트는 JWT와 함께 애플리케이션 서버에 요청
애플리케이션 서버는 인증 서버의 private key를 모르므로 JWT를 검증할 수 없음
각 애플리케이션 서버에 인증서버의 private key를 넣어놓으면 되긴한다.
비대칭키 암호화 방식에서의 인증서버 구축하기
흐름을 살펴보자.
인증 서버가 클라이언트에게 JWT를 발급
클라이언트는 JWT와 함께 애플리케이션 서버에 요청
애플리케이션 서버는 인증 서버의 public Key를 통해 JWT를 검증할 수 있음
각 애플리케이션 서버에 일일히 key를 넣어줄 필요가 없다. public key가 공개되어 있기 때문이다.
API Gateway가 존재한다면?
비대칭키 암호화 방식을 사용하면 매번 각 서버에서 필터나 인터셉터를 통해 JWT에 대한 검증을 수행할 것이다.
하지만 API Gateway가 존재하면 API GW에서만 검증하면 된다. API GW에서 public key를 통해 검증해도 되지만, 대칭키 방식을 사용해도 API GW에만 private key를 넣어주면 되므로 대칭키 방식의 문제점도 딱히 드러나지 않는다.
간단히 결론을 내려보면 API GW가 없다면 비대칭키 암호화 방식을 사용하는게 좋고, API GW가 존재한다면 어떤 방식을 쓰든 상관없을 것 같다는 생각이 든다.

Refresh Token은 토큰이 탈취당할 경우를 대비해 사용되는 것이다.
Access Token은 언제든지 탈취될 수 있다고 가정하기 때문에 Access Token에는 중요한 정보를 담으면 안된다.
HS256, RS256 알고리즘에서 공통적으로 쓰이는 단어인 'S256' 이라는 단어는 SHA256 알고리즘을 의미합니다.
또한, SHA256 은 데이터 무결성을 위해 사용되는 암호화 해쉬 알고리즘(함수) 입니다.
SHA256 알고리즘에 대해 중요한 부분을 정리해보자면, SHA256 알고리즘은 "암호화 해쉬 알고리즘"에 속하고, "복호화가 불가능"하다는 특성을 가집니다.
SHA256 알고리즘은 SHA(Secure Hash Algorithm) 알고리즘의 한 종류로서 256비트로 구성되며 64자리 문자열을 반환합니다.
SHA-256은 미국의 국립표준기술연구소(NIST; National Institute of Standards and Technology)에 의해 공표된 표준 해시 알고리즘인 SHA-2 계열 중 하나이며 블록체인에서 가장 많이 채택하여 사용하고 있습니다.
이름에 내포되어 있듯 2^256 만큼 경우의 수를 만들 수 있습니다.
개인용 컴퓨터로 무차별 대입을 수행해 해시 충돌 사례를 찾으려고 할 때 많은 시간이 소요될 정도로 큰 숫자이므로 충돌로부터 비교적 안전하다고 평가됩니다.
해쉬는 임의의 크기를 가진 데이터를 고정된 데이터의 크기로 변환시키는 함수를 말합니다.
해쉬 알고리즘에는 SHA256 만 있는 것은 아니고, SHA512, SHA3 등 다양한 해쉬 알고리즘이 있습니다.
그러나 해쉬 알고리즘을 유용하게 사용하려면 총 5가지 요구조건이 있습니다.
라이브러리 다른 거 쓰면 안되더이다
쇄도 효과를 이미지로 나타내면, 눈사태 이미지를 예시로 들 수 있습니다.
쇄도 효과는 해쉬 알고리즘에서 매우 중요한 요건으로 쇄도 효과란 입력값이 아주 작은 변화만 있어도 해쉬값이 완전히 달라지는 것을 의미합니다.
쇄도 효과라고 불리는 이유는 알고리즘 구현 방식에 있습니다.
요점은 하나의 변화가 몇 가지의 추가적인 변화를 유발하고, 그 변화가 더 많은 변화를 유발하고 이는 더 많은 변화를 유발한다는 것입니다.
즉, 눈사태와 같다고 보면 될 것 같습니다.
해쉬값은 고정된 길이를 가집니다. 이는 매우 다양한 조합을 만들 수 있지만, 제한적인 것에는 변함이 없습니다.
수학에서는 이러한 원리를 비둘기집 원리라고 합니다.
만약 10마리의 비둘기가 있고 집은 9개만 있다면 하나의 집에는 2마리의 비둘기가 들어가야 합니다.
가끔 그런 일이 벌어져도 아무런 영향이 없을 것입니다. 최종적으로 충돌 저항성 조건이 의미하는 바는 알고리즘이 인위적인 충돌에 견딜 수 있어야 한다는 뜻입니다.
HS256 알고리즘은 JWT 토큰의 암호화 알고리즘으로 많이 쓰이는 암호화 알고리즘 입니다.
앞에서 HS256에서 S256은 SHA256 을 의미한다고 했습니다.
H는 무엇일까요? HMAC 알고리즘을 의미합니다. HMAC 알고리즘은 대칭키 암호화 알고리즘입니다.
HS256 = HMAC + SHA256
서버에서 Header, Payload 는 Base64로 인코딩된다. (참고로, Base64 알고리즘은 복호화가 되는 알고리즘입니다)
Base64 는 Binary 데이터를 아스키 코드 일부와 일대일로 매칭되는 문자열로 단순 치환되는 인코딩 방식이다.
- Base64 는 Binary 데이터를 아스키 코드 일부와 일대일로 매칭되는 문자열로 단순 치환되는 인코딩 방식이다.
- Base64는 6bit당 2bit의 OverHead의 발생으로 기존 데이터보다 30%이상 길어지게 되며 여기에 Encoding, Decoding의 로직까지 추가되므로 성능에도 영향을 끼칠 수 있다.
- Base64를 사용하는 가장 큰 이유는 Binary 데이터를 텍스트 기반 규격으로 다룰 수 있기 때문이다. JSON과 같은 문자열 기반 데이터 안에 이미지 파일등을 Web에서 필요로 할때 Base64로 인코딩하면 UTF-8과 호환 가능한 문자열을 얻을 수 있다. 끝에 '='과 같은 패딩 기호가 있다면 이는 구분자로써 사용되므로 대부분 Base64로 생각할 수 있다.
- 기존 ASCII 코드는 시스템간 데이터를 전달하기에 안전하지 않다. 모든 Binary 데이터가 ASCII 코드에 포함되지 않으므로 제대로 읽지 못한다. 반면 Base64는 ASCII 중 제어문자와 일부 특수문자를 제외한 53개의 안전한 출력 문자만 이용하므로 데이터 전달에 더 적합하다.
출처: https://blue-boy.tistory.com/227public class Base64Test{ //인코딩 public static string Base64Encoding(string data) //UTF-8 try{ byte[] byteTemp = new byte[data.length]; byteTemp = System.Text.Encoding.UTF8.GetBytes(data); string encodedData = Convert.Tobase64String(byteTemp); return byteTemp; }catch(Exception) { Console.WriteLine("Error"); } } //디코딩 public static stirng Base64Decoding(string data) //UTF-8 { try{ System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding(); System.Text.UTFEncoding(); byte[] decodeToByte = Convert.FromBase64String(); char[] decodedChar = new char[charCnt]; utf8Decode.GetChars = new char[charCnt]; string result = new String(decodeChar); return result; }catch(Exception) { Console.WriteLine("Error"); } }
Signature 는 Header + Payload + secret 값을 HS256 알고리즘으로 암호화됩니다.
HS256 (Base64(Header) + Base64(Payload) + secret key) 가 됩니다.생성된 Header, Payload, Signature 로 JWT 토큰을 만들어 클라이언트로 보내고, 클라이언트는 로컬 스토리지에 토큰을 저장합니다.
클라이언트는 서버에 요청이 있을 경우, 토큰과 요청 내용을 같이 보냅니다.
서버에서는 Header 와 Payload 를 Base64 알고리즘으로 복호화한 뒤, 서버만 알고 있는 개인키를 가지고 다시 HS256 알고리즘을 이용해 암호화해보고, 클라이어트에서 보낸 토큰과 같은지 유효성 검증을 합니다.
만약 다르다면, 유효하지 않은 토큰
같다면, 유효한 토큰
RS256에서 R은 RSA 를 의미합니다.
RSA 알고리즘은 비대칭키 암호화 알고리즘입니다.
비대칭키 암호화 알고리즘은 공개키(public key)와 개인키(private key)를 이용해 암호화를 진행합니다.
RS256 = RSA + SHA256
서버에서 Header, Payload 는 Base64로 인코딩된다. (참고로, Base64 알고리즘은 복호화가 되는 알고리즘입니다)
Header, Payload는 서버의 개인키로 암호화 Signature를 만듭니다.
토큰을 만들어 클라이언트에 보냅니다.
클라이언트는 서버에 요청을 보낼 때 토큰과 요청 내용을 같이 보냅니다.
서버에서는 토큰의 유효성 검증을 하기 위해서, 공개키로 Signature 를 복호화해봅니다.
공개키로 암호화한 것은 개인키로 복호화 가능 ('데이터 암호화' 기능)
개인키로 암호화한 것은 공개키로 복호화 가능 ('전자서명' 기능)