
SameSite=Lax|Strict, Secure, HttpOnlyhttp
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
);
⚠️ 기본 헤더명은 "X-XSRF-TOKEN"이므로, "X-CSRF-TOKEN"을 사용하려면
CookieCsrfTokenRepository에서 직접 지정해주어야 한다. (repo.setHeaderName("X-CSRF-TOKEN");)
| 종류 | 공격 방식 |
|---|---|
| Stored XSS (저장형) | 악성 스크립트를 서버에 저장 후, 사용자가 해당 데이터를 불러올 때 실행 |
| Reflected XSS (반사형) | 악성 스크립트를 요청 파라미터나 URL에 담아 서버 응답에 즉시 반영 |
| DOM XSS | 서버 응답을 거치지 않고, 클라이언트 측(JavaScript)에서 DOM 조작 시 발생 |
th:text) 사용innerHTML 등 신뢰되지 않은 HTML 직접 삽입 금지http.headers(h -> h
// CSP 설정
.contentSecurityPolicy(csp -> csp.policyDirectives(
"default-src 'self'; " +
"script-src 'self'; " +
"object-src 'none'; " +
"base-uri 'self'; " +
"frame-ancestors 'none'"))
.xssProtection(x -> x.disable()) // 구형 XSS 필터 비활성 → CSP로 대체
.contentTypeOptions(withDefaults()) // MIME 타입 스니핑 방지
.frameOptions(frame -> frame.deny()) // 클릭재킹 방지
// HTTPS 강제
.httpStrictTransportSecurity(hsts -> hsts
.maxAgeInSeconds(31536000) // 1년
.includeSubdomains(true)
)
);
migrateSession(기본값)JSESSIONID가 URL에 노출되지 않도록 설정HttpOnly, Secure, SameSitehttp.sessionManagement(sm -> sm
.sessionFixation(sf -> sf.migrateSession()) // 로그인 시 세션 재발급
.maximumSessions(1) // 동시 세션 제한
.maxSessionsPreventsLogin(false)
);
server:
servlet:
session:
tracking-modes: cookie # URL에 JSESSIONID 붙는 것 차단
cookie:
same-site: Lax
secure: true
jti 기반)HttpOnly , Secure, SameSite + CSRF 방어iss, aud, exp 등 검증http
.oauth2ResourceServer(o -> o
.jwt(jwt -> jwt.jwtAuthenticationConverter(customConverter))
)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
);
// jti 기반 재사용 차단
boolean isRevoked(String jti){ return redis.hasKey("revoked:"+jti); }
void revoke(String jti, Duration ttl){ redis.set("revoked:"+jti, "1", ttl); }
⚠️ JWT는 기본적으로 서명(Signature)으로 위·변조 여부만 검증하며, 암호화는 하지 않는다.
누구나 Payload를 디코딩할 수 있으므로, 민감 정보는 절대 포함하면 안된다.
.(점)으로 구분된 세 부분(Header,Payload,Signature)으로 구성된다. xxxxx.yyyyy.zzzzz
↑ ↑ ↑
Header Payload Signature
토큰의 타입(typ)과 서명 알고리즘(alg), 필요 시 키 식별자(kid)를 포함한다.
예시
{
"alg": "RS256",
"typ": "JWT",
"kid": "key-2025-08"
}
alg: 서명 알고리즘 (예: HS256, RS256, ES256) -> none 사용 금지 ❌typ: 토큰 타입 (일반적으로 "JWT")kid: Key ID — 키 로테이션 시 어떤 키를 사용할지 식별토큰에 담길 클레임(Claim) 정보로 인증/인가에 필요한 식별자, 권한, 메타데이터 등을 포함한다.
예시
{
"sub": "user-123",
"name": "Gil Dong",
"exp": 1754803200,
"iat": 1754796000,
"scope": "read write",
"roles": ["USER", "ADMIN"],
"email": "user@example.com"
}
1) 표준 클레임 (Registered Claims) : 사전에 정의된 예약 키
iss (issuer): 토큰 발급자sub (subject): 토큰 주체 (사용자 ID)aud (audience): 토큰 대상exp (expiration): 만료 시각iat (issued at): 발급 시각nbf (not before): 토큰 활성화 시작 시각jti (jwt id) : 토큰 고유 식별자 (재사용 감지에 활용)2) 공개 클레임 (Public Claims) : 표준 외의 일반 키, 충돌 방지 위해 네임스페이스(주로 URL 형태) 권장
3) 비공개 클레임 (Private Claims) : 발급자와 소비자 간 합의된 키 (ex. roles, scope 등)
Header와 Payload가 위/변조되지 않았는지를 검증한다.
서명 방식
예시
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)