본문 바로가기

TIL

TIL 240624 Cookie

Spring Security를 잘 이해하려면 Cookie, Session, Token(Json Web Token아님)부터 알아야한다

 

  • 인증/인가
    • Spring Filter → 굉장히 잘 사용한 경우(추상화 된 기술) Spring Security

 

Cookie

-웹 브라우저(크롬, 엣지, 파이어폭스 등)에서 사용자의 컴퓨터에 저장되는 정보

- 이 정보는 서버에서 사용자의 브라우저로 전송되고, 브라우저는 해당 정보를 저장해두었다가 이후에 같은 서버에

 요청을 보낼 때마다 함께 전송한다

- 쿠키는 사용자의 상태나 세션을 유지하거나 사용자 경험을 개선하기 위해 사용된다

 

Response 예시

set-cookie:

sessionId=abcd;

expires=Sat, 11-Dec-2023 00:00:00 GMT;

path=/; domain=notion.so;

Secure

 

  • 사용자 로그인 세션 관리, 광고 트래킹 등에 주로 사용된다.
  • Cookie는 모든 HTTP 요청이 발생할 때 마다 항상 서버에 전송된다.
    • 네트워크 트래픽이 추가적으로 발생된다.
    • 최소한의 정보만 사용해야한다.(sessionId, 인증 token)
    • 보안에 취약하다.
  • 서버에 전송하지 않고 브라우저에 단순히 데이터를 저장하고 싶다면?
    • Web Storage를 사용한다. (localStorage, sessionStorage)
      • 보안에 취약하다.
      • 보안에 취약하기 때문에 주민번호와 같은 민감정보를 저장하면 안된다.

브라우저 개발자도구 (F12)

- 브라우저에 저장된 Cookie 들을 확인할 수 있다

 

Cookie Header

  • Set-Cookie : Server에서 Client로 Cookie 전달(Response Header)
  • Cookie : Client가 Server에서 받은 Cookie를 저장하고 HTTP 요청시 Server로 전달(Request Header)

Cookie의 생명주기

  • Set-Cookie: expires=Sat, 11-Dec-2023 00:00:00 GMT;
    • 해당 만료일이 도래하면 쿠키가 삭제된다.
  • Set-Cookie: max-age=3600 (second, 3600초는 한시간. 60 * 60)
    • 0이 되거나 음수를 지정하면 쿠키가 삭제된다.
  • 세션 Cookie : 만료 날짜를 생략하면 브라우저 완전 종료시 까지만 유지된다.(Default)
    • ex) 브라우저를 완전 종료 후 다시 페이지를 방문했을 때 또 로그인을 해야된다
  • 영속 Cookie : 만료 날짜를 입력하면 해당 날짜까지 유지

 

Cookie의 도메인

쿠키가 아무 사이트에서나 생기고 동작하면 안된다!(필요없는 값 전송, 트래픽 문제 등)

  • ex) domain=notion.so
  • 명시한 문서 기준 도메인 + 서브 도메인 포함
    • domain=notion.so를 지정하여 쿠키 생성
    • notion.so + dev.notion.so와 같은 서브 도메인에도 쿠키 접근
  • 생략하면 현재 문서 기준 도메인만 적용

 

Cookie의 경로

  • 1차적으로 도메인으로 필터링 한다. Path는 2차 필터
  • ex) 일반적으로 path=/ 루트(전체)로 지정한다
  • 위 경로를 포함한 하위 경로 페이지만 쿠키 접근
  • path=/api
    • /api → 가능
    • /api/example → 가능
    • /example → 불가능
    •  

Cookie 보안

  • Secure
    • 기본적으로 Cookie는 http, https 구분하지 않고 전송한다.
    • Secure를 적용하면 https인 경우에만 전송한다. s = Secure
  • HttpOnly
    • XSS 공격을 방지한다(배운내용 입니다 여러분..).

    • 자바스크립트에서 접근 불가(브라우저 Console에 document.cookie 사용 불가)
    • HTTP 전송시에만 사용

 

로그인 상태유지

  • 한번 로그인에 성공하면 HTTP Response에 쿠키를 담아서 브라우저에 전달한다.
  • 브라우저는 요청마다 Cookie를 함께 전송한다.
  • 실제로는 보안상의 문제로 userId=1과 같은 index 정보를 저장한다. → 이것 또한 보안문제가 있음
    • 세션 Cookie : 만료 날짜를 생략하면 브라우저 완전 종료시 까지만 유지된다.(Default)
      • ex) 브라우저를 완전 종료 후 다시 페이지를 방문했을 때 또 로그인을 해야된다
    • 영속 Cookie : 만료 날짜를 입력하면 해당 날짜까지 유지

요구사항에 맞추어 세션 Cookie를 사용할지 영속 Cookie를 사용할지 결정하면 된다.

 

 

@Entity
public class User {
	private Long id;

	private String userId; // 로그인 ID

	private String password; // 로그인 비밀번호	

	private String name;
}


@Getter
public class LoginRequest { // DTO
	
	@NotBlank	
	private String id; // 사용자가 입력한 아이디

	@NotNull
	private String pwd; // 사용자가 입력한 비밀번호

}


@Getter
public class UserDto { // Response
	private Long id;
	// 이외 응답에 필요한 데이터들을 필드로 구성하면 된다.
	// 필요한 생성자
}


@Controller
@Requiredargsconstructor
public class UserController {
	
	private final UserService userService;

	@PostMapping("/login")
	public String login(
					@Valid @ModelAttribute LoginRequest request,
					HttpServletResponse response // 쿠키값 세팅에 필요
	) {
		// 로그인 유저 조회
		UserDto loginUser = userService.login(request.getId(), request.getPwd());

		if (loginUser == null) {
			// 로그인 실패 예외처리
			return "login";
		}
		
		// 로그인 성공 처리
		// 쿠키 생성, Value는 문자열로 변환하여야 한다.
		Cookie cookie = new Cookie("userId", String.valueOf(loginMember.getId()));
		
		// 쿠키에 값 세팅 (expire 시간을 주지 않으면 세션쿠키가 됨, 브라우저 종료시 로그아웃)
		// Response Set-Cookie: userId=1 형태로 전달된다.
		response.addCookie(cookie);

		return "redirect:/"; // 메인페이지로 리다이렉트
	}
	
}


@Service
@Requiredargsconstructor
public class UserService {

	private final UserRepository userRepository;

	public UserDto login(String inputId, String inputPwd) {

		// 1. 유저 조회
		// 2. 입력한 값과 유저의 비밀번호 일치여부 조회
		// 3. 일치하지 않으면 예외처리
		// 3-1. 일치하면 return Type인 UserDto에 맞추어 Controller로 전달
		return new UserDto(user);
	}

}

 

  • 로그인에 성공하면 쿠키를 생성하고 HttpServletResponse 객체에 에 담는다.
    • Response Header를 확인해보면 Set-Cookie: userId=1 형태로 전달된다.
  • Cookie 이름(Key값)은 userId 이고 회원 index 값을 담아둔다.
  • 만료 시간을 지정하지 않아서 세션 쿠키로 만들어진다.
    • 브라우저 종료 전까지 userId 가 모든 Request Header Cookie에 담겨서 전달된다.

 

 

  • 저장된 쿠키를 사용하는 API 예시
    • 요구사항
      • 로그인한 회원이면 home 페이지로 이동(메인(home) 페이지를 보려면 로그인 필수)
      • 로그인하지 않은 회원이면 login 페이지로 이동
@Controller
@Requiredargsconstructor
public class HomeController {

	private UserService userService;
	
	@GetMapping("/")
	public String home(
				// @CookieValue(required = true) 로 필수값(default) 설정
				// required = false 이면 필수값 아님.
				@CookieValue(name = "userId", required = false) Long userId, // String->Long 자동 타입컨버팅
				Model model
	) {
		
		// 쿠키에 값이 없으면 로그인 페이지로 이동 -> 로그인 X
		if(userId == null) {
			return "login";
		}
		
		// 실제 DB에 데이터 조회 후 결과가 없으면 로그인 페이지로 이동 -> 일치하는 회원정보 X
		UserDto loginUser = userService.findById(userId);

		if(loginUser == null) {
			return "login";
		}

		// 정상적으로 로그인 된 사람이라면 View에서 사용할 데이터를 model 객체에 데이터 임시 저장
		model.addAttribute("loginUser", loginUser);
		// home 화면으로 이동
		return "home";
		
	}
}

 

 

쿠키의 보안 문제

  1. 쿠키 값은 임의로 변경할 수 있다.
    1. 위 예시코드와 같은 방식을 사용하면 Client가 임의로 쿠키의 값을 변경하면 서버는 다른 유저로 인식한다. userId = 내맘대로 수정가능
  • 브라우저 → 개발자도구 → Application 탭 → Storage/Cookies/URL → 값 조정 가능
  • 실제로는 암호화되어 저장되어있는 Value들을 볼 수 있다!

 

보안 대처방법

  1. 쿠키에 중요한값을 저장하지 않는다.
  2. 사용자 별로 일반 유저나 해커들이 알아보지 못하는 값을 노출한다. → 보통 암호화된 Token을 쿠키에 저장한다. → Access Token, Refresh Token
  3. 서버에서 암호화된 Token과 사용자를 매핑해서 인식한다.
  4. Token은 서버에서 관리한다.(생성[만료시간 등], 검증)
  5. 토큰은 해커가 임의의 값을 넣어도 동작하지 않도록 만들어야 한다. → JWT : ADSFSADFKJLJ!@#LKAJSDFLKAJSASDASDFASD
  6. 해커가 토큰을 탈취해도 사용할 수 없도록 토큰 만료시간을 짧게 설정한다.
  7. 탈취가 의심되는 경우 해당 토큰을 강제로 만료시키면 된다. ex) 접속기기 혹은 IP가 다른 경우 등 → Logic

 

'TIL' 카테고리의 다른 글

TIL 240625 Session  (0) 2024.06.26
TIL 240621 Validation  (0) 2024.06.24
TIL 240617 과제 피드백 AOP  (0) 2024.06.18
TIL 240614 Mockito, 통합테스트  (0) 2024.06.17
TIL 240613 단위 테스트  (1) 2024.06.14