티스토리 뷰
Spring 기반 웹 애플리케이션에서 발생할 수 있는 4가지 주요 보안 공격 (CSRF, XSS, 세션 고정, JWT 탈취)에 대해 설명하고, 각각에 대한 Spring Security 또는 일반적인 대응 전략을 설명하세요.
1. CSRF (Cross-Site Request Forgery)
브라우저가 쿠키를 자동으로 전송한다는 점을 악용해서 사용자가 자신의 의지와 무관하게 공격자가 의도한 행위(수정, 삭제 등)를 특정 웹사이트에 요청하게 만드는 공격

대응 전략
- Referer 체크
요청이 신뢰할 수 있는 도메인에서 온 것인지 서버 측에서 검증
- SameSite 쿠키
키 설정에 SameSite=Lax 또는 Strict를 부여하여 타 사이트에서의 쿠키 전송을 제한
- CSRF Token
서버가 임의의 토큰을 발급하고, 클라이언트가 매 요청(POST, PUT 등)마다 이 토큰을 포함해야만 정상적인 요청으로 인정
2. XSS (Cross-Site Scripting)
게시판이나 댓글창에 악성 스크립트를 삽입하여, 다른 사용자의 브라우저에서 그 스크립트가 실행되게 함 이를 통해 세션 쿠키를 탈취하거나 페이지를 변조하는 공격

대응 전략
- 입력값 검증
Lucence나 Naver Lucy XSS Filter 같은 라이브러리를 사용하여 입력 단계에서 위험한 태그를 제거
- 출력 이스케이프
<script>를 <script>처럼 치환하여 브라우저가 코드를 실행하지 않고 '문자'로만 출력하게 함
- Security Headers
CSP(콘텐츠 보안 정책) 등을 설정하여 브라우저가 신뢰할 수 없는 스크립트 실행을 스스로 차단하게 명령
3. 세션 고정 (Session Fixation)
공격자가 자신의 세션 ID를 희생자에게 강제로 주입한 뒤, 사용자가 로그인하면 해당 세션 ID를 그대로 사용하여 사용자의 권한을 가로채는 방식

대응 전략
- 로그인 시 세션 갱신
사용자가 로그인 성공 시, 기존 세션을 무효화하고 새로운 세션 ID를 발급
- Spring Security 설정
sessionManagement().sessionFixation().changeSessionId() 설정을 통해 로그인할 때마다 ID를 바꾸도록 강제
4. JWT 탈취 (JWT Hijacking)
JWT는 서버 세션과 달리 한 번 발급되면 제어가 어려움. 로컬 스토리지에 저장된 토큰이 XSS 등으로 탈취되면 공격자가 자유롭게 API를 호출할 수 있게 됨

대응 전략
- HttpOnly & Secure 쿠키
토큰을 로컬 스토리지 대신 쿠키에 저장하되, 자바스크립트가 접근하지 못하게(HttpOnly) 막고 HTTPS에서만 전송되게(Secure) 설정
- Refresh Token 도입
Access Token의 수명을 아주 짧게(예: 15분) 잡고, 탈취되어도 피해를 최소화, 대신 더 긴 수명의 Refresh Token을 통해 새 토큰을 발급
- RTR (Refresh Token Rotation)
Refresh Token을 한 번 쓰면 바로 폐기하고 새것을 발급하여, 탈취된 토큰의 재사용을 원천 봉쇄
JWT(JSON Web Token)의 구조와 각 구성 요소가 어떤 역할을 하는지 구체적으로 설명하세요.
JWT란?
정보를 JSON 객체 형태 안전하게 전송하기 위한 하나의 규격(표준)
쉽게 말해, 서버가 사용자를 인증한 뒤 "이 사람은 홍길동이 맞습니다"라는 정보를 담은 '전자 신분증'을 발급해 주는 것이라고 생각하면 됨
JWT의 구조
점(.)으로 구분된 Header, Payload, Signature 세 부분으로 구성
ex) AAAAAAAAAA.BBBBBBBBBBBBB.CCCCCCCCCC
JWT 구성 요소와 역할

1. Header(헤더) : 어떤 방식으로 만들었는가?
가장 윗부분에 위치하며, 토큰을 어떻게 해석해야 하는지에 대한 정보를 담음
- typ (Type) : 보통 "JWT"라고 적히고, 토큰의 유형을 나타냄
- alg (Algorithm) : 서명을 생성할 때 사용한 암호화 알고리즘(예: HS256, RS256)을 명시함. 이 정보가 있어야 나중에 서버가 토큰을 검증할 수 있ㄷ음
2. Payload(페이로드) : 어떤 정보를 담고 있는가?
토큰의 핵심 부분으로 실제 사용자 정보나 권한 등의 데이터가 들어감.
여기에 담기는 데이터 하나하나를 클레임(Claim)이라고 부르고 크게 세가지로 나뉘어짐.
- 등록된 클레임(Registered Claims)
JWT 표준에서 정의된 미리 정의된 클레임들 필수는 아니지만 권장되며, 상호 운용성을 위해 사용한다
{
"iss": "https://auth.example.com", // 발급자 (Issuer)
"sub": "12345", // ⭐주체 (Subject) - 사용자 ID
"aud": "https://api.example.com", // 대상 (Audience) - 토큰을 사용할 서비스
"exp": 1639137000, // ⭐만료 시간 (Expiration Time)
"iat": 1639050600, // ⭐발급 시간 (Issued At)
"nbf": 1639050600, // 유효 시작 시간 (Not Before)
"jti": "abc123" // JWT ID (고유 식별자)
}
- sub : 사용자 식별, 가장 중요한 정보
- exp : 토큰 만료 처리의 핵심, 보안상 필수
- iat : 토큰 발급 시간 추적, 로그 분석에 유용
다양한 등록된 클레임이 존재하지만 위 세 클레임은 실무에서 자주 사용된다
- 공개 클레임(Public Claims)
개발자가 마음대로 정의할 수 있지만, 다른 사람들이 만든 토큰의 정보와 이름이 겹치지 않도록(충돌 방지) 주의해야 하는 클레임
충돌을 막기 위해 보통 URI(인터넷 주소) 형식으로 이름을 길게 지어서 사용
ex :
{"https://my-service.com/jwt_claims/is_admin": true}
- 비공개 클레임(Private Claims)
특정 애플리케이션 내에서만 사용하는 커스텀 클레임. 가장 자유롭고 실무에서 많이 사용됨
ex :
{
"userId": wj,
"email": "wj@wj.com",
"role": "super_star"
}
주의사항 : 페이로드는 암호화된 것이 아니라 단순히 'Base64'라는 방식으로 인코딩(글자 변환)만 되어 있기 때문에 누구나 쉽게 원래 글자로 풀어서 읽을 수 있기 때문에(디코딩), 비밀번호나 주민등록번호 같은 민감한 개인정보는 절대 담으면 안됨.
3. Signature(서명) : 이 토큰은 진짜인가?
가장 중요한 보안 장치 [헤더 + 페이로드 + 서버만 아는 비밀 키]를 합쳐서 헤더에서 지정한 알고리즘으로 암호화한 값
토큰이 변조되지 않았음을 증명함
- 무결성 검증 : 토큰 내용이 변조되었는지 확인
- 인증 : 토큰이 신뢰할 수 있는 발급자가 생성했는지 검증
- 부인 방지 : 토큰 발급자가 발급 사실을 부인할 수 없음
