쿠키 및 세션은 편리하나, 보안위험이 매우 크다. 보안 관련해선 여기를 보면 된다. (https://dean83.tistory.com/380)
기본적으로 한계가 있기 때문에 주로 JWT 같은 토큰 기반으로 인증을 한다.
쿠키
- 서버가 사용자의 브라우저에 저장을 요청하는 작은 텍스트 데이터 조각.
- 탈취 될 수 있으므로, 보안 속성 설정이 필수임.
- Secure 설정을 통해 Https 프로토콜 에서만 쿠키를 전송하도록 강제해야 한다.
- HttpOnly 설정을 통해 자바스크립트에서 쿠키 접근을 원천 차단 할 수 있다.
- CSRF 공격 방어를 위해 Strict 옵션을 이용하거나, x-csrf-token 값을 이용할 수 있다.
- 주 목적은 서버가 클라이언트를 실별 하기 위한 수단임
- HTTP 통신은 상태를 저장하지 않으므로 사용
- 구조 : key=value 형식의 문자열 쌍으로 구성
- 예 : theme=dark
- Set-Cookie 명령을 통해 저장
- Response header 에 서버가 프론트엔드로 리턴할때 담아서 리턴
- 생명주기 및 전송범위를 지정 할 수 있음.
- Expires(만료날짜), Max-Age(받은 후 부터의 지속시간) 속성이 있는 경우 해당 기간동안 브라우저가 종료되어도 보관
- 해당 값이 없으면 브라우저 종료시 자동으로 삭제됨.
- Domain : 쿠키가 전송될 서버 도메인 지정 (지정을 안하면 현재 도메인)
- Path : 서버내의 URL 경로 지정. (지정 안하면 /)
- Expires(만료날짜), Max-Age(받은 후 부터의 지속시간) 속성이 있는 경우 해당 기간동안 브라우저가 종료되어도 보관
아래는 Springboot 에서 추가하는 법에 대한 예 이다.
import org.springframework.http.ResponseCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
public class CookieController {
@GetMapping("/set-cookie")
public ResponseEntity<String> setCookie() {
ResponseCookie cookie = ResponseCookie.from("userToken", "abc123")
.domain("example.com") // 쿠키가 전송될 도메인
.path("/") // 쿠키가 유효한 path
.maxAge(60 * 60 * 24) // 1 day (expires 대신 maxAge 사용 권장)
.httpOnly(true) // JS 접근 불가
.secure(true) // HTTPS에서만 전송
.sameSite("Strict") // SameSite 옵션
.build();
return ResponseEntity.ok()
.header(HttpHeaders.SET_COOKIE, cookie.toString())
.body("cookie set");
}
}
세션
세션의 경우, 쿠키와 다르게 상태를 서버에 저장한다. 따라서 민감한 정보나 보안이 필요한 정보를 세션으로 저장한다.
메모리, DB, 세션스토어(Redis 등 외부 저장소) 같은 곳에 저장을 할 수 있다. SessionID를 통해 사용자 인증을 할 수 있다.
임시 데이터로 사용되며, 주로 세션스토어를 통해 Redis나 Memcached에 저장하여 사용(다중 서버간 공유를 위해)한다. 인증이 필요한 접근인 경우 매번 SessionID를 저장한 곳에서 가져와 비교해야 한다.
* Redis나 Memcached등 외부저장소를 이용하기 위해서는 Spring Session 을 이용하여 설정 하면 된다.
세션ID는 각 브라우저 별로 동일한 유저가 로그인 했다 하더라도 각각 발급을 한다.
즉, 다음의 과정을 거친다.
로그인 -> SessionID 저장 -> 쿠키에 해당 SessionID 전송 -> 다음 요청시 전달 받은 SessionID 유효성 검사 -> 다음 응답
-> 로그아웃 요청시, 세션 저장소에서 해당 SessionID 삭제 -> 서버가 클라이언트 응답을 통해 쿠키를 즉시 만료하여 삭제 시킴
(Set-Cookie: sessionid=; Max-Age=0;)
세션을 발급하는 예는 다음과 같다.
@PostMapping("/login")
public ResponseEntity<String> login(
@RequestParam String username,
HttpServletRequest request
) {
// 세션 생성 (없으면 생성)
HttpSession session = request.getSession(true);
// 세션에 값 저장
session.setAttribute("username", username);
session.setAttribute("loginTime", System.currentTimeMillis());
// 세션 ID
String sessionId = session.getId();
return ResponseEntity.ok("세션 발급 완료! sessionId = " + sessionId);
}
세션을 확인하는 예는 다음과 같다.
@GetMapping("/me")
public ResponseEntity<String> me(HttpServletRequest request) {
// 기존 세션 가져오기 (없으면 null)
HttpSession session = request.getSession(false);
if (session == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body("세션이 없습니다. 로그인하세요.");
}
String username = (String) session.getAttribute("username");
if (username == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body("세션 정보가 유효하지 않습니다.");
}
return ResponseEntity.ok("로그인한 사용자: " + username);
}
로그아웃하여 세션아이디를 삭제하는 예는 다음과 같다.
@PostMapping("/logout")
public ResponseEntity<String> logout(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate(); // 세션 삭제
}
return ResponseEntity.ok("로그아웃 완료!");
}
쿠키와 연계하여, 기본적으로 JSESSIONID 쿠키를 알아서 발급해 주나, 커스터마이징을 할 수 있다.
아래는 커스터마이징의 예 이다.
import jakarta.servlet.SessionCookieConfig;
import jakarta.servlet.ServletContext;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SessionCookieCustomizer implements ServletContextInitializer {
@Override
public void onStartup(ServletContext servletContext) {
SessionCookieConfig config = servletContext.getSessionCookieConfig();
config.setName("JSESSIONID"); // 기본값
config.setHttpOnly(true); // 자바스크립트 접근 금지
config.setSecure(true); // https에서만 전송
config.setPath("/"); // 경로
config.setDomain("example.com"); // 필요하면 설정
config.setMaxAge(60 * 60 * 24); // 1일
}
}'Backend > SpringBoot' 카테고리의 다른 글
| [Spring Security] MethodSecurity 및 커스텀 핸들러 (0) | 2025.12.16 |
|---|---|
| [Spring Security] JWT 사용시 Filter 구성 예 (0) | 2025.12.15 |
| @SQLRestriction 및 논리 삭제 (0) | 2025.12.11 |
| Retryable, 낙관적 락 간편 적용하기 (0) | 2025.12.09 |
| Flyway 사용하기 (0) | 2025.12.04 |