개요
이번 포스팅에서는 로그인 기능을 구현할 때 꼭 필요한 개념인 쿠키와 세션에 대해 정리해보겠습니다.
실제 로그인과 관련된 코드 및 설명은 다음 글에서 정리하겠습니다.
쿠키
- 서버에서 요청에 대한 응답으로 클라이언트로 전달해주는 정보
- 클라이언트가 서버에서 받은 쿠키를 저장하고, HTTP 요청 시 서버로 전달하는 정보
- 위 두 정보는 HTTP Header에 저장이 됨
- Set-Cookie: 서버에서 클라이언트로 쿠키 전달
- Cookie: 클라이언트가 서버에서 받은 쿠키를 저장하고, HTTP 요청 시 서버로 전달하는 정보
쿠키 - 생명주기
- Set-Cookie 내 expires라는 필드를 참고하면 쿠키 만료 날짜 및 시간을 알 수 있음 (GMT 기준)
- Set-Cookie 내 max-age 필드는 마지막 요청으로부터 몇 초 동안 쿠키가 유지되는지 확인 가능
- 세션 쿠키: 만료 날짜를 생략할 경우 브라우저 종료 시까지 유지
- 영속 쿠키: 만료 날짜를 입력하면 해당 날짜까지 유지
쿠키 - domain
- 도메인을 명시할 경우 명시한 도메인 및 서브 도메인 내에서만 쿠키 접근 가능
- domain = naver.com일 경우
- naver.com 뿐만 아니라 sports.naver.com에도 쿠키 접근 가능
- 도메인을 생략할 경우 현재 도메인에만 쿠키 접근 가능
- domain = naver.com일 경우
- naver.com 에는 접근 가능하지만 sports.naver.com 에는 접근 불가능
쿠키 - path
- 경로를 명시하지 않는 경우 path = / 이기 때문에 모든 페이지에 접근 가능
- 경로를 명시하는 경우 경로를 포함한 하위 경로 페이지만 쿠키가 접근 가능
- path = /home으로 지정된 경우
- /home, /home/1, /home/1/2와 같이 home 하위 경로 접근 가능
- /newhome 접근 불가능
쿠키를 사용하지 않는 상태에서 로그인하는 경우
- 예상 결과: 클라이언트가 로그인 한 이후부터는 해당 애플리케이션 내 다른 페이지로 가더라도 로그인한 상태로 이동
- 실제 결과: 클라이언트가 로그인할 때만 서버가 로그인한 유저를 인식하고 다른 페이지로 가면 또다시 로그인하지 않은 상태라고 인식
- 이러한 일이 발생하는 이유는 HTTP가 기본적으로 Stateless 프로토콜이기 때문
- 클라이언트와 서버 간 요청과 응답을 주고 받은 이후에는 연결이 끊어짐
- 클라이언트가 다시 요청 시 서버는 이전 요청에 대한 정보를 지니고 있지 않음
- 정리를 하자면, 클라이언트와 서버는 서로 상태를 유지하지 않음
* 클라이언트와 서버각 지속 연결 상태를 유지하도록 설정했더라도 만료 시간이 존재
해결책
- 그렇다면 매 요청마다 쿼리 파라미터로 로그인한 유저의 정보를 전달하면 해결이 되지 않을까?
- 이럴 경우 모든 요청에 사용자 정보가 포함되야하기 때문에 개발 공수가 어렵고
- 보안 문제를 야기할 수 있기 때문에 현실적으로 어려움 (쿼리 파라미터를 통해 사용자 정보 유추 가능)
- 따라서, 쿠키를 사용하여 위 문제를 해결하자
쿠키를 사용한 상태에서 로그인할 경우
- 서버는 로그인 요청이 성공할 경우 응답 헤더 내 Set-Cookie로 사용자 정보 전달
- 웹브라우저 내 쿠키저장소가 있으므로 브라우저는 사용자 정보를 쿠키 저장소에 저장
- 클라이언트에서 요청 시 쿠키 저장소에서 쿠키 꺼내 모든 요청에 자동으로 포함 (네트워크 트래픽을 추가로 유발하는 것이 단점)
- 모든 요청에 자동으로 쿠키를 포함할 경우 쿼리 파라미터에 사용자 정보를 전달하는 것처럼 보안 문제 야기
- 쿠키 값은 클라이언트에서 강제로 변경 가능
- 해커가 Man in the Middle Attack 방식을 사용하여 쿠키 값을 탈취 후 다른 값을 쿠키에 넣어 전송 가능
- 따라서, 주민등록번호, 신용카드 번호, 비밀번호 등과 같이 보안에 민감한 데이터는 쿠키에 저장하면 안 됨
- 또한, 쿠키 값이 규칙적이어서 유추가 가능하면 사용자가 쿠키 값을 위변조 하여 다른 고객 정보를 탈취도 가능
- id=1, 2, 3,... 이런 식으로 쿠키 값이 전달될 경우 쿠키 내 id 값을 변경하여 다른 고객 정보 확인 가능
- 따라서, UUID와 같이 랜덤 값을 쿠키에 저장하고 해당 값과 사용자 정보를 서버 내에서 1:1 매칭을 시켜 사용자 정보를 서버 내에서만 알 수 있도록 처리하는 것이 중요
* 아래 예시처럼 쿠키에 id 값을 그대로 노출시켜서는 안 되지만 간단한 예시이므로 그림에서는 id 값을 전달한 것처럼 표현
쿠키 보안 부연 설명
- 기본적으로 쿠키는 http, https 프로토콜을 구분하지 않고 전송이 되지만 Secure 옵션을 적용하면 https인 경우에만 전송 가능
- HttpOnly 옵션
- XSS 공격 방지 목적
- 자바스크립트에서 접근 불가
- HTTP 전송에만 사용
- SameSite 옵션
- XSRF 공격 방지 목적
- 요청 도메인과 쿠키에 설정된 도메인이 같은 경우만 쿠키 전송
- 비교적 최신 버전 브라우저에만 적용 가능하므로 브라우저가 지원하는지 확인 후 적용 필요
세션
- 쿠키는 클라이언트에서 저장하는 반면, 세션은 서버에서 저장하고 있는 정보
- 세션은 아래와 같이 크게 3가지 기능 제공
- 세션 생성
- 각 클라이언트 고유 세션 ID를 부여하며 이는 UUID처럼 중복될 확률이 매우 적음
- 세션 ID는 세션 저장소 내 보관
- 세션 ID로 응답 쿠키를 생성해 클라이언트에 전달
- 세션 조회
- 클라이언트가 요청을 전달한 쿠키 내 세션 ID를 통해 세션 저장소에 보관한 값 조회
- 세션 만료
- 클라이언트가 요청으로 전달한 쿠키 내 세션 ID를 통해 세션 저장소에 보관한 값을 찾고 제거
- 세션 생성
로그인 시 쿠키와 세션 모두 사용
- 앞서 쿠키만 사용할 경우 여러 가지 보안 문제를 야기할 수 있었음
- 아래와 같이 세션과 쿠키를 사용하여 보안 문제를 어느 정도 방지 가능
위와 같이 세션과 쿠키를 모두 사용할 경우 아래와 같은 보안 문제 해결 가능
- 쿠키 값이 변조 가능하지만 sessionId는 불규칙적이기 때문에 다른 고객 정보가 나올 확률이 매우 적음
- 쿠키는 클라이언트 사이드에 저장되기 때문에 노출되기 쉽지만 sessionId는 노출이 돼도 위험도가 매우 적음
- sessionId가 불규칙적이더라도 man in the middle attack을 통해 탈취한 sessionId를 그대로 사용할 경우 고객 정보 노출될 위험이 존재
- 세션의 만료시간을 짧게 유지하거나 ip가 갑자기 중국, 북한, 러시아 ip로 바뀔 경우 세션 값을 만료시키는 방법으로 대처 가능
* 서버 용량이 허용된만큼 세션을 생성할 수 있으나 가급적 최소한의 정보만 세션 내 저장하도록 하는 것을 권장합니다.
HttpSession 부연 설명
- 세션을 생성하거나 조회할 때 HttpServletRequest의 getSession 메서드 파라미터로 boolean 값이 넘어가는데 디폴트는 true
- true를 넘길 때는 세션이 있을 경우 기존 세션을 반환하고
- 기존 세션이 없을 경우 새로운 세션을 생성해서 반환
- 따라서, 신규로 세션을 생성할 때는 파라미터를 굳이 넘기지 않아도 됨
- 반면, 세션을 조회하는 목적일 경우 신규로 세션을 생성할 필요가 없으므로 getSession 메서드 파라미터로 false를 넘김
- 세션 데이터를 보관할 때는 HttpServletRequest의 setAttribute 메서드를 통해 key, value 형식으로 저장, 세션 데이터를 조회할 때는 getAttribute 메서드에 파라미터로 key 값을 전달하여 조회
- ConcurrentHashMap과 비슷한 동작 방식
- 서블릿을 통해 HttpSession 생성 시 JSESSIONID의 이름으로 쿠키가 생성되며 추정하기 매우 어려운 랜덤 값이 포함됨
- 로그인을 처음 시도하면 URL 뒤에 jsessionid가 쿼리파라미터로 붙는 것을 확인할 수 있음
- 이는 해당 브라우저가 쿠키를 지원하는지 유무를 모르기 때문에 URL에 jsessionid를 같이 전달하는 방식
- 쿠키를 지원하는 것을 확인한 뒤로는 다음 요청부터는 jsessionid가 쿼리 파라미터로 전달 안됨
- 쿠키를 지원하지 않을 경우 모든 요청에 jsessionid가 쿼리파라미터로 전달되면서 쿠키 역할을 하게 함
- 위 현상을 방지하기 위해서는 기존 게시글 참고 https://jaimemin.tistory.com/1516
출처
HTTP 웹 기본지식 (김영한 강사님)
인프런 스프링 MVC 2편 (김영한 강사님)
반응형
'Spring' 카테고리의 다른 글
[SpringBoot] Filter, Interceptor 개념 정리 및 로그인 처리 (4) | 2021.08.01 |
---|---|
[SpringBoot] 쿠키, 세션을 이용한 로그인 처리 (2) | 2021.07.31 |
[SpringBoot] Validation 간단 정리 - 2 (Bean Validation) (0) | 2021.07.25 |
[SpringBoot] Validation 간단 정리 - 1 (BindingResult, Validator) (6) | 2021.07.16 |
[Spring Boot] 메시지, 국제화 간단 정리 (0) | 2021.07.10 |