- 인증(Authentication)
- 사용자가 누구인지 확인하는 과정
ex) 로그인- 인가(Authorization)
- 사용자가 어떤 권한을 가지고 있는지 결정하는 과정
- 인증 선행 필수
ex) 회원만 조회 가능한 게시글, 본인이 작성한 게시글 수정
사용자의 웹 브라우저에 저장되는 정보로 사용자의 상태 혹은 세션을 유지하거나 사용자 경험을 개선하기 위해 사용. 사용자 세션 관리(로그인, 장바구니, 접속 시간), 광고 트래킹(사용자 행동)등이 포함됨.
- 쿠키를 사용하는 이유
- HTTP는 Stateless, Connectionless 특성을 가지고 있기 때문에 Client가 재요청시 Server는 이전 요청에 대한 정보를 기억하지 못함
- 로그인과 같이 상태를 유지해야 하는 경우 발생
- Request에 사용자 정보를 포함하면 해결
- 로그인 후에는 사용자 정보와 관련된 값이 저장되어 있어야 함
- 브라우저를 완전히 종료한 뒤 다시 열어도 사용자 정보가 유지되어야 함
💡 쿠키의 위치
브라우저 개발자 도구(F12) → Application → Cookies
- 로그인 성공시 응답
- Set-Cookie
- 로그인시 전달된 ID, Password로 User 테이블 조회하여 일치여부 확인
- 일치한다면 Set-Cookie를 활용해 Cookie에 사용할 값 저장
- 로그인 이후 요청
- 요청 헤더
Cookie : 사용자 정보
- 로그인 이후의 모든 요청에는 Request Header에 항상 Cookie 값을 추가
- 네트워크 트래픽이 추가적으로 발생
- 최소한의 정보만 사용 해야 함
- Cookie에 담겨있는 값으로 인증/인가를 진행
서버에서는 HTTP 응답 헤더에 Set-Cookie 속성을 사용해 생성하고 설정할 수 있음.
- Cookie Header
- Set-cookie
- Server에서 Client로 Cookie 전달(Response Header)
- Cookie
- Client가 Cookie를 저장하고 HTTP 요청시 Server로 전달(Request Header)
// Response 알아보기 set-cookie: sessionId=abcd; expires=Sat, 11-Dec-2024 00:00:00 GMT; path=/; domain=abcde.com; Secure
- Cookie의 생명주기
- 세션 Cookie
- 만료 날짜(
expires
,max-age
)를 생략하면 브라우저 완전 종료시 까지만 유지(Default
)- 브라우저를 완전 종료 후 다시 페이지를 방문했을 때 재로그인 필요
- 영속 Cookie
- 만료 날짜를 입력하면 해당 날짜까지 유지
expires=Sat, 11-Dec-2024 00:00:00 GMT;
(해당 만료일이 되면 쿠키가 삭제됨)max-age=3600
(초단위, 0이 되거나 음수를 지정하면 쿠기가 삭제됨)- Cookie의 도메인
- 쿠키가 아무 사이트에서나 생기고 동작하면 안되기 때문에 설정
- 필요없는 값 전송, 트래픽 문제 등이 발생
domain=abcde.com
를 지정하여 쿠키를 저장dev.abcde.com
와 같은 서브 도메인에서도 쿠키에 접근- domain을 생략하면 현재 문서 기준 도메인만 적용
- Cookie의 경로
- 1차적으로 도메인으로 필터링 후 Path 적용
- 일반적으로
path=/
루트(전체)로 지정- 위 경로를 포함한 하위 경로 페이지만 쿠키에 접근
path=/api
지정
path=/api/example
가능path=/example
불가능- Cookie 보안
Secure
- 기본적으로 Cookie는 http, https 구분하지 않고 전송
- Secure를 적용하면 https인 경우에만 전송
HttpOnly
- XSS(Cross-site Scripting) 공격 방지
- 악성 스크립트를 웹 페이지에 삽입하여 다른 사용자의 브라우저에서 실행되도록 하는 공격
- 자바스크립트에서 Cookie 접근 불가
- HTTP 요청시 사용
SameSite
- 브라우저 지원 여부 확인필요
- CSRF(Cross-Site Request Forgery) 공격 방지
- 사용자가 의도하지 않은 상태에서 특정 요청을 서버에 전송하게 하여 사용자 계정에서 원치 않는 행동을 하게 만듦
- 요청 도메인과 쿠키에 설정된 도메인이 같은 경우에만 쿠키 전송
- 쿠키 값을 임의로 변경 가능
- Client가 임의로 쿠키의 값을 변경하면 서버는 다른 유저로 인식
- 브라우저 개발자도구(F12) → Application → Cookies → 값 수정 가능
- Cookie에 저장된 Data는 탈취되기 쉬움
- 네트워크 전송 구간에서 탈취될 확률 매우 높음
- HTTPS를 사용하는 이유 중 하나
- 민감한 정보를 저장하면 안됨
- 한번 탈취된 정보는 변경이 없다면 반영구적으로 사용 가능
- 대처 방법
- 쿠키에 중요한값을 저장하지 않음
- 일반 유저나 해커들이 알아보지 못하는 값을 노출
- 일반적으로 암호화된
Token
을 쿠키에 저장- 서버에서 암호화된
Token
과 사용자를 매핑해서 인식- Token은 서버에서 관리
- 토큰은 해커가 임의의 값을 넣어도 동작하지 않도록 만들어야 함
- 토큰이 탈취 당해도 사용할 수 없도록 토큰 만료시간을 짧게 설정
- 탈취가 의심되는 경우 해당 토큰을 강제로 만료시킴
- 접속기기 혹은 IP가 다른 경우 등
Cookie를 사용한 방식은 여러가지 보안 문제가 있기 때문에 중요한 정보는 모두 서버에서 저장. Client와 서버는 예측 불가능한 임의의 값으로 연결.
- Session 생성 순서
- 로그인에 성공하면 Server에서 임의로 만든 Session ID를 생성
- Session ID는 예측 불가능 해야 함(ex. UUID)
- 생성된 Session ID와 조회한 User 인스턴스를 서버의 Session 저장소에 저장
- 서버에 유저와 관련된 중요한 정보를 저장
- Session 동작 순서
- 로그인
- 상태유지를 위해 Cookie 사용
- 서버는 클라이언트에
Set-Cookie: SessionId=임의생성값
을 전달- 클라이언트는 Cookie 저장소에 전달받은
SessionId
값을 저장- 로그인 이후 요청
- 클라이언트는 모든 요청에 Cookie의 SessionId를 전달
- 서버에서는 전달된 SessionId로 Session 저장소를 조회
- 로그인 시 저장하였던 Session 정보를 서버에서 사용
- Session 특징
- Session을 사용하여 서버에 민감한 정보를 저장
- 예측이 불가능한 세션 ID를 사용하여 쿠키값을 변조해도 문제 없음
- 세션 ID에 중요한 정보는 포함되어 있지 않음
- 시간이 지나면 세션이 만료되도록 설정
- 해킹이 의심되는 경우 해당 세션을 제거
- 데이터를 저장하는 곳이 클라이언트가 아닌 서버라는 것이 쿠키와 다른 점
- Servlet은 Session을 자체적으로 지원
- @SessioinAttribute
- 세션을 새로 생성하는 기능은 없음
- 이미 로그인이 완료된 사용자를 찾는 경우, 즉 세션이 있는 경우에 사용
@Controller @Requiredargsconstructor public class SessionHomeController { private UserService userService; @GetMapping("/v2/session-home") public String homeV2( // 로그인 여부를 확인해야하므로 required = false @SessionAttribute(name = Const.LOGIN_USER, required = false) UserResponseDto loginUser, Model model) { // session에 loginUser가 없으면 Login 페이지로 이동 if (loginUser == null) { return "session-login"; } // Session이 정상적으로 조회되면 로그인된것으로 간주 model.addAttribute("loginUser", loginUser); // home 화면으로 이동 return "session-home"; } }
- HttpSession
- 세션을 간편하게 사용할 수 있도록 다양한 기능 지원
@Slf4j @RestController public class SessionController { @GetMapping("/session") public String session(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session == null) { return "세션이 없습니다."; } // jsessionId 값 조회 log.info("session.getId()={}", session.getId()); // 세션의 유효 시간(초단위, 기본값은 1800) log.info("session.getMaxInactiveInterval()={}", session.getMaxInactiveInterval()); // 세션 생성 시간 log.info("session.getCreationTime()={}", session.getCreationTime()); // 해당 세션에 마지막으로 접근한 시간 log.info("session.getLastAccessedTime()={}", session.getLastAccessedTime()); // 새로 생성된 세션인지 확인 log.info("session.isNew()={}", session.isNew()); return "세션 조회 성공!"; } }
Session은 logout 기능을 사용하여 session.invalidate();
가 되어야 삭제되지만 대부분의 사용자들은 로그아웃을 굳이 하지않고, 브라우저를 종료함.
- Session의 문제점
- HTTP는 Connectionless 특성을 가지고 있어서 서버가 브라우저의 종료 여부 판별 불가
- 서버에서 언제 세션을 삭제해야 하는지 판단 불가
- JSESSIONID의 값을 탈취 당한 경우 해당 값으로 악의적인 요청 가능
- 세션은 서버 메모리에 생성되고 자원은 한정적이기 때문에 꼭 필요한 경우만 생성 필요
- Session 생명 주기
- 기본적으로 30분을 기준으로 삭제
- 실제 로그인 후 30분 이상의 시간동안 사용중인 사용자의 세션도 삭제됨
- 재로그인 해야 하는 경우 발생
- HttpSession 사용
- 세션 생성 시점으로부터 30분이 아닌 서버에 최근 Session을 요청한 시간을 기준으로 30분 유지
Session은 서버의 메모리를 사용하여 확장성이 제한됨.
- 서버가 DB 혹은 메모리에 저장된 세션 정보를 매번 조회하여 오버헤드 발생
- 서버가 상태를 유지해야 하므로 사용자 수가 많아질수록 부담 증가
- Cookie는 웹 브라우저에만 존재하여 모바일 앱 등의 다양한 클라이언트에서 인증 처리 불가
- Scale Out(수평적 확장)에서 서버간 세션 공유가 여려움
💡 오버헤드(Overhead)란?
어떤 처리를 하기 위해 들어가는 간접적인 처리 시간, 메모리 등을 의미한다.