0. JWT란?
JWT(JSON Web Token)는 정보를 안전하게 전송하기 위한 간단한 토큰 기반의 오픈 표준이다.
Header(헤더), Payload(페이로드), Signature(서명) 세 부분으로 구성되어 있으며........... 는 잠시 제쳐두고!!
JWT가 어떻게 쓰고, 어떻게 생겼는지. 보다는
"왜 사용되고, 어디에 사용하는지"가 더욱 중요하다!!!
항상 공부를 할 때, 블로그에서 긁어와서 코드를 돌아가게 만드는 것은 의미가 없다고 생각하는 편이다.
근본적으로 왜 사용되고, 어디에 사용되고, 그 이후에 어떻게 사용하는지를 배우는 서순이 맞다고 생각한다.
1. Session에 대해서
세션(Session)은 웹 애플리케이션에서 클라이언트와 서버 간의 지속적인 상태를 유지하기 위한 메커니즘이다.
HTTP 프로토콜은 기본적으로 상태를 유지하지 않는(stateless) 프로토콜이다!
즉, "상태를 기억할 수 없는 금붕어 같은 친구"라는 것이다...
클라이언트의 각 요청은 서버에 의해 완전히 독립적으로 처리된다.
하지만 세션을 사용하면 클라이언트의 여러 요청 간에 상태를 공유할 수 있다.
만약에 해당 client가 server에 방문한 적이 있는지!? 에 관한 이야기라면,
아주 간단한 방법으로, Session에 "방문한 적 있음 카드"를 작성해서 client에게 Response와 함께 주면 된다.
그리고 "너 다음에 방문할 때는 이거 들고와!"라는 방식을 쓰는 것이다.
1-1. 클라이언트가 서버에 최초 접근한 경우
1-2. Request Header에 Session ID를 들고 접근한 경우
1-3. Session이 사라지는 경우
1) 서버 측에서 Session을 강제로 지우는 경우
2) 사용자가 브라우저를 종료할 때
3) Session 만료 시간이 지났을 때 (보통 30분)
1-4. 보통 Session이 사용되는 로그인 요청 & 인증
Session ID : 1234는 (이미 접근한 계정)
Session ID : 3456은 최초 접근 계정이라고 가정하자.
- 1 ~ 4 : 최초 접근
- 5 ~ 10 : 로그인
- 이후 "user 정보가 필요한 페이지" 요청
* 정리
민감한 정보를 요청할 경우,
요청 헤더에 있는 Session ID를 확인해서 / 해당 User인지를 판별하고
필요한 정보를 제공해준다.
1-5. Session의 단점
Client가 몇 백 만명 이라면....?
동시 접속자 수가 서버가 처리할 수 있는 인원이 넘어버리면..?
-> 나머지는 모두 대기ㅠㅠ
=> 자주 사용하는 해결책이 "서버를 여러개 두는 것 ; Load Balancing"
그런데 문제는 여기서 발생한다.
서버를 여러개 두면, Session 저장소도 각각 따로 두게 된다는 점
1번 서버에 첫 요청을 보내놓고, 2번 서버에서 재요청을 하게 되면,
2번 서버는 해당 Client를 처음 온 친구로 인식하게 된다는 점.
1-6. 해결책
- Sticky Session을 이용해 그 사용자가 갔던 서버로만 보내는 것이다.
- 모든 서버에 세션이 생성 될 때마다 세션을 복제 시킨다. (동기화)
- DB에 세션 정보를 저장한다. (DB에는 IO가 일어나기 때문에 속도가 엄청나게 저하..)
-> 세션 사용의 장점을 파괴ㅋㅋ
메모리 공유 서버 (RAM)를 이용한다! (주로 Redis를 사용) - JWT를 사용하면 이 문제들을 해결 할 수 있다.
2. TCP에 대해서
컴퓨터 네트워크의 내용이다... 컴공생들은 아는 내용 ㅠ 간단하게 하고 넘어가겠다!
2-1. OSI 7계층
- 4번 째인 Transport 계층에서 TCP/UDP 통신 여부를 결정한다.
2-2. TCP/UDP
1) TCP (Transmission Control Protocol):
신뢰성: TCP는 연결 기반의 프로토콜로, 데이터 전송 시 신뢰성을 중요시 한다!
데이터가 전송되면 수신 측에서 확인 응답(ACK)을 보내고, 만약 손실이나 오류가 발생하면 재전송을 수행!
순서 보장: TCP는 데이터의 전송 순서를 보장한다.
흐름 제어와 혼잡 제어
연결 지향
속도 매우 느림
2) UDP (User Datagram Protocol):
신뢰성: UDP는 비연결성 프로토콜로, 신뢰성이 TCP보다 낮다. 오류가 발생하면 복구나 재전송이 이루어지지 않는다.
순서 보장 없음: UDP는 데이터의 순서를 보장하지 않는다.
흐름 제어와 혼잡 제어 없음
연결 지향이 아님
빠른 전송 속도 : 실시간에 자주 쓰임
-> 우리 웹은 TCP/IP 통신을 한다!
3. CIA에 대해서 (보안)
3-1. CIA
CIA는 정보 보안의 기본 원칙을 나타내는 약어이다.
각각 Confidentiality(기밀성), Integrity(무결성), Availability(가용성)을 나타낸다.
데이터/ 네트워크 보안 등에서 매우 자주 들어본 단어들일 것이다.
1) Confidentiality (기밀성):
정의: 기밀성은 정보에 대한 무단 접근을 방지, 오직 허가된 사용자나 시스템만이 해당 정보에 접근할 수 있도록 하는 원칙
목표: 민감한 정보가 무단으로 노출되는 것을 방지하여 정보의 비밀성을 유지한다.
2) Integrity (무결성):
정의: 무결성은 정보가 정확하고 변조되지 않았음을 보장하는 원칙이다.
목표: 정보가 변경되거나 조작되지 않도록 하여 데이터의 신뢰성을 보장한다.
3) Availability (가용성):
정의: 가용성은 정보나 시스템이 필요할 때 항상 사용 가능한 상태를 유지하는 원칙이다.
즉, 서비스의 지속적인 이용이 보장되어야 한다.
목표: 서비스 중단을 최소화하고 사용자가 필요할 때 항상 정보나 서비스에 접근 가능하도록 해야 한다.
---
쉽게 말해서, A -> B 로 데이터를 전송한다고 해보자.
1. 해커가 데이터를 중간에 빼돌렸다. -> 기밀성 위반
2. 해커가 데이터를 중간에 변조시켰다. -> 무결성 위반
3. 해커가 DDoS 공격 등으로 원활이 전송하지 못하게 하였다. -> 가용성 위반
3-2. CIA를 지키는 방법
1) Data를 보낼 때, 함께 데이터를 암호화하여 같이 보낸다.
-> 복호화할 수 있는 해커가 있다면 무용지물
2) Data를 보낼 때, 암호화하고 보낸다. (Key 사용해야 복호화 가능)
-> 해커가 열 수는 없지만, 실제 수령자도 Key 없이는 열 수 없다..
-> "Key 전달 문제 발생"
⬇️
일단은 문제는 있지만 2의 방식을 사용하면 ,
해커가 A의 메시지를 낚아채서 위조하는 문제는 해결할 수 있다.
그렇다면?
⬇️
예시 :
- A가 B에게 내일 10시에 보자고 보냈다.
- B의 입장에서는 ACK(OK Sign)를 받았다.
- 하지만, B는 나오지 않았다.
문제 상황의 2가지 이유 :
1) C가 A의 메시지를 낚아채서, 변형시켰을 수도 있다. (열쇠로 잠그면 1은 해결 가능하다고 했다.)
2) 혹은 C가 A의 메시지를 낚아채서 도착하지 못하게 한 뒤,
그냥 헛소리를 A가 보낸 것인 B에게 보내버릴 수 있다.
(메시지가 무엇인지는 모르지만 혼란스러운 메시지)
2) C가 A의 메시지를 낚아채서, B에게 도달하지 않았음에도 ACK를 보냈을 수도 있다.
"답장이 누구로부터 왔는지" 에 대한 문제
즉, "Key 전달 문제", "누구로부터 왔는지에 대한 문제" 2 가지만 해결하면 CIA를 지킬 수 있다!
4. RSA에 대해서
4-1. RSA
RSA(알앤샤)는 공개키 암호 시스템의 하나이다.
공개키(public key)와 비밀키(private key)를 사용하여 암호화에 사용되는 키 쌍을 생성하는 알고리즘이다.
공개키는 모두에게 공개되어 있으며, 누구나 이를 사용하여 메시지를 암호화할 수 있다. (암호화는 자유야~)
비밀키는 암호화된 메시지를 복호화하는 데 사용되며, 이 키는 소유자만이 알고 있어야 합니다.
RULE:
공개키로 암호화를 했다면, 개인키로만 열 수 있다. (Data 암호화에 자주 사용)
개인키로 암호화를 했다면, 공개키로만 열 수 있다. (전자 서명에 자주 사용 ; "나임을 인증")
4-2. Key 전달 문제 해결
RSA를 사용하여 A가 B에게 보낼 Data를 암호화 한다고 가정하자.
이제는 A가 B의 공개키로 암호화된 Data를 보내면, B의 비밀키로만 열 수 있게 된 것이다!!
(C는 가로채도 열 수가 없다 ㅠㅠ)
4-3. 누구로부터 왔는지에 대한 문제 해결
B는 A가 보냈다는 것도 확인해야 한다!
그렇다면, 이전에 B의 공개키로 암호화된 Data를 보낼 때, A의 개인키로 암호화를 한 번 더 시키는 것이다.
그럼 B는 A가 보낸 것인지를 A의 공개키로 확인할 수 있다.
B의 답장 또한 B의 개인키로 암호화하면 된다!
해당 RSA 알고리즘을 사용한 것이 JWT이다.
https://jinhos-devlog.tistory.com/entry/JWT%EB%9E%80
5. CORS에 대하여
<해당 포스트를 읽고 오길 바란다!>
5-1. JWT의 토큰 인증 방식
세션을 사용했을 때의 문제점을 해결할 수 있다!
Cookie를 사용하지 않아도 된다! (쿠키 탈취의 취약점 사라짐 -> csrf 비활성화.)
JWT는 클라이언트가 서버에 인증된 상태를 유지하기 위해 사용될 때 일반적으로 쿠키를 사용하지 않는다는 말이다.
클라이언트 측은 다음 번 서버에 접근할 때 발급받은 Access Token을 들고 가야 하기 때문에,
이 토큰을 쿠키나, 세션 등에 저장할 수 있다.
서버를 분산시켜도 {🔑secret_key} 만 알고 있으면 된다!
조금 더 정확히 말하면, csrf에 대한 보호가 아예 필요 없다는 것이 아니다.
스프링 시큐리티에서는 기본적으로 csrf 토큰을 이용해서 방어를 하고 있다.
매 요청마다 csrf 토큰은 서버에서 임의의 난수를 생성하여 클라이언트에게 넘겨주는 방식이다.
클라이언트는 서버로 요청할 때 발급받은 csrf 토큰과 함께 Request를 보내면 서버에서는 csrf 토큰을 비교하여 위조된 요청인지 판별하게 되는 것이다.
브라우저에서 보내는 요청은 세션 쿠키를 포함하는 모든 쿠키를 자동으로 포함하기 때문에 CSRF 공격이 가능할 수 있다!
그렇기 때문에, 쿠키 탈취의 취약점이 있다는 것이다 !!!
csrf.disable(); -> JWT 토큰 사용
+ 마지막으로 추가...
그렇다면 JWT 토큰이 CSRF 공격 자체를 막을 수 있다는 것인가?
JWT는 보안 공격을 막기 위한 것이 주 목적이 아니기 때문에 모든 보안 관련 상황에서 충분하지 않을 수 있다.
JWT 토큰을 Local Storage에 토큰을 저장하고 bearer token으로 사용한다면 CSRF 공격에 대한 대응은 될 수 있다만, XSS 공격에는 취약할 수 있다.
.... 이야기가 너무 길어진다.
어쨌든 다양한 방법으로 보안 공격을 방지하는 메커니즘을 구현하고 관리할 필요성이 있다는 것.
5-2. 여러 도메인에서의 Session/Cookie !?
1) 기본 구조
2) 도메인이 여러 개라면?
<도메인 간(Cross-Origin)에서의 쿠키 전송에 관한 내용>
... CORS
아까 서버를 여러 개 두었을 때의 단점도 이야기 했다! -> 이것도 해결 가능하다
5-3. CORS(Cross-Origin Resource Sharing)
간단히 말하면 이런 것이다.
웹 페이지에서 실행 중인 스크립트가
다른 출처의 자원에 접근할 수 있도록
브라우저에게 허용하는 보안 기능
브라우저는 보안상의 이유로 동일 출처 정책(Same-Origin Policy)를 따르기 때문에,
한 출처에서 불러온 웹 페이지가 다른 출처의 자원에 접근하는 것을 제한한다.
* 쉽게 말해, 도메인이 다른 서버로 리소스 요청을 보내면, 쿠키를 전송시키지 않는다!
* 다른 도메인으로의 리다이렉션외에도
javascript에서 외부 도메인에서의 AJAX 요청에서 브라우저가 쿠키를 전송하지 않을 수 있다.
해결방법
CORS는 이러한 보안 정책으로 인해 발생하는 문제를 해결하기 위해 도입되었다.
CORS는 웹 브라우저에서 다른 출처의 리소스에 안전하게 접근할 수 있도록 허용하는 보안 메커니즘이다.
- CORS 정책에 따라 브라우저는 보안상의 이유로 다른 도메인으로의 AJAX 요청 시에는 쿠키를 자동으로 전송하지 않는다고 했다.
- 그러나, JavaScript 코드에서 직접 XMLHttpRequest 또는 Fetch API를 사용하여 AJAX 요청을 보낼 때는 이러한 제약이 적용되지 않을 수 있다. ("HTTP Only = False" 로 풀어준다면.)
fetch("http://www.naver.com", {
headers: {
Cookie: "~~"
}
}).then();
- 즉, JavaScript 코드에서 직접 조작하면서 요청 헤더에 쿠키를 추가하여 보낼 수 있다.
- 이 경우에는 브라우저의 보안 정책(CORS)를 우회하는 것이므로, 신뢰할 수 없는 도메인으로부터의 요청에 대한 쿠키 전송을 막기 위해 "HTTP Only = False" 로 풀지 않고 True로 설정하는 편이다.
<외부 장난질 가능해짐.. 보안성 DOWN>
=> headers에 Anthorization를 사용하는 방식!
6. Basic / Bearer Authentication
6-1. Basic Authentication
- 타입(Type): Basic
- 토큰 형식(Credentials): Base64로 인코딩된 사용자 ID와 비밀번호
- 보안 취약성: Basic 토큰은 사용자 ID와 비밀번호가 Base64로 인코딩(RFC 7617)
토큰이 노출되면 사용자의 인증 정보가 노출 가능 -> 보안에 취약 - 사용 예시:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
- Basic Authentication은 브라우저가 제공하는 기본 인증 팝업이나 HTTP 클라이언트 라이브러리를 통해 쉽게 사용할 수 있다.
6-2. Bearer Token
- 타입(Type): Bearer
- 토큰 형식(Credentials): 주로 JWT(RFC 7519) 또는 OAuth 토큰(RFC 6750) 사용
- 보안 취약성: Bearer 토큰은 토큰에 ID와 PW를 내장하지 않기 때문에 기본적으로 Basic 보다는 보안적으로 강력합니다.
- 사용 예시:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- 서버는 세션 저장소가 필요 없고, (여러 개의 서버를 둬도 상관 없다는 말!!!)
토큰 자체에 사용자 정보가 포함되어 있어 STATELESS하며,
무결성과 보안성이 강조되는 특징이 있다!