내배캠 TIL 31일차

오병택·2025년 4월 1일

내배캠

목록 보기
53/73

학습 요약

schedulev2

schedulev2 lv1~lv3

현재 진행사항

Schedulev2는 일정 생성까지, Writer는 회원가입, 모든 유저들 조회까지

클래스들도 많고 인터페이스도 많아서 여기에 코드를 다 올리기엔 지저분 할 것 같아서 깃허브 주소를 남기겠습니다. 원치 않게 lv1부터 lv3을 통틀어서 하고 있네요
깃허브 주소 -> https://github.com/byeongtaek12/ScheduleProject-Spring-JPA


Spring 숙련 - 2주차

사용자의 웹 브라우저에 저장되는 정보로 사용자의 상태 혹은 세션을 유지하거나 사용자 경험을 개선하기 위해 사용

Cookie를 사용하는 이유

  1. HTTP는 Stateless, Connectionless 특성을 가지고 있음
  2. Client가 재요청시 Server는 이전 요청에 대한 정보를 기억하지 못함
  3. 로그인과 같이 상태를 유지해야 하는 경우가 발생
  4. Request에 사용자 정보를 포함하면 해결
    • 로그인 후에는 사용자 정보와 관련된 값이 저장되어 있어야 함
  5. 브라우저를 완전히 종료한 뒤 다시 열어도 사용자 정보가 유지되어야 함

로그인 성공시 응답

로그인 이후 요청

  • 로그인 이후에는 모든 요청마다 Request Header에 항상 Cookie 값을 담아서 요청
    • 네트워크 트래픽이 추가적으로 발생
    • 최소한의 정보만 사용해야 함

서버에서는 HTTP 응답 헤더에 Set-Cookie 속성을 사용해 생성하고 설정할 수 있음

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

Response

java
 set-cookie: 
 **sessionId**=abcd; 
 **expires**=Sat, 11-Dec-2024 00:00:00 GMT;
 **path**=/; 
 **domain**= ~.kr;
 **Secure**
  • Cookie의 생명주기

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

    • 쿠키가 아무 사이트에서나 생기고 동작하면 안 됨
      • 필요없는 값 전송, 트래픽 문제 등이 발생
    • domain= ~.kr
      • domain= ~.kr를 지정하여 쿠키를 저장
      • dev.~.kr와 같은 서브 도메인에서도 쿠키에 접근
    • domain을 생략하면 현재 문서 기준 도메인만 적용
  • Cookie의 경로

    • 1차적으로 도메인으로 필터링 후 Path가 적용
    • 일반적으로 path=/ 루트(전체)로 지정
    • 위 경로를 포함한 하위 경로 페이지만 쿠키에 접근
  • Cookie 보안

    1. Secure
      • 기본적으로 Cookie는 http, https 구분하지 않고 전송
      • Secure를 적용하면 https인 경우에만 전송
    2. HttpOnly
      • XSS(Cross-site Scripting) 공격을 방지
      • 자바스크립트에서 Cookie 접근을 못하도록 막음
      • HTTP 요청시 사용
    3. SameSite
      • 비교적 최신 기능이라 브라우저 지원여부를 확인 해야 함
      • CSRF(Cross-Site Request Forgery) 공격을 방지
      • 요청 도메인과 쿠키에 설정된 도메인이 같은 경우만 쿠키 전송

Cookie로 로그인 상태 유지

Cookie는 주로 사용자 세션 관리(로그인, 장바구니, 접속시간)나 광고 트래킹(사용자 행동) 등의 목적으로 사용

  1. 쿠키 값은 임의로 변경할 수 있음
    • Client가 임의로 쿠키의 값을 변경하면 서버는 다른 유저로 인식
  2. Cookie에 저장된 Data는 탈취되기 쉬움
    • userId = 주민번호, userId = 인덱스 값
    • 쿠키는 네트워크 전송 구간에서 탈취될 확률이 매우 높음
      • HTTPS를 사용하는 이유 중 하나에 속함
      • 민감한 정보를 저장X
    • 한번 탈취된 정보는 변경이 없다면 반영구적으로 사용할 수 있음
  • 보안 대처방법
    1. 쿠키에 중요한 값을 저장X
    2. 사용자 별로 일반 유저나 해커들이 알아보지 못하는 값을 노출
      • 일반적으로 암호화된 Token을 쿠키에 저장한다.
      • 서버에서 암호화된 Token과 사용자를 매핑해서 인식
      • Token은 서버에서 관리
    3. 토큰은 해커가 임의의 값을 넣어도 동작하지 않도록 만들어야 함
    4. 해커가 토큰을 탈취해도 사용할 수 없도록 토큰 만료시간을 짧게 설정
    5. 탈취가 의심되는 경우 해당 토큰을 강제로 만료시키면 됨

Session 1강

보안 문제를 해결하려면 중요한 정보는 모두 서버에서 저장해야 하고 Client와 서버는 예측이 불가능한 임의의 값으로 연결해야 함

Session

서버에서 중요한 정보를 보관하며 로그인 연결을 유지하는 방법

Session 생성 순서


1. 로그인에 성공하면 Server에서 임의로 만든 Session ID를 생성
- Session ID는 예측이 불가능해야 함
- UUID와 같은 값을 활용
2. 생성된 Session ID와 조회한 User 인스턴스를 서버의 Session 저장소에 저장
- 서버에 유저와 관련된 중요한 정보를 저장

Session 동작 순서

  1. 로그인
  • 상태유지를 위해 Cookie를 사용
    • 서버는 클라이언트에 Set-Cookie: SessionId=임의생성값 을 전달
    • 클라이언트는 Cookie 저장소에 전달받은 SessionId 값을 저장
  • Sessions을 사용하면 유저와 관련된 정보는 클라이언트에 없음
  1. 로그인 이후 요청
  • 클라이언트는 모든 요청에 CookieSessionId를 전달
  • 서버에서는 Cookie를 통해 전달된 SessionId로 Session 저장소를 조회
  • 로그인 시 저장하였던 Session 정보를 서버에서 사용

Session 특징

  1. Session을 사용하여 서버에서 민감한 정보들을 저장
    • 예측이 불가능한 세션 ID를 사용하여 쿠키값을 변조해도 문제X
    • 세션 ID에 중요한 정보는 들어있지 않음
    • 시간이 지나면 세션이 만료되도록 설정
    • 해킹이 의심되는 경우 해당 세션을 제거하면 됨
  2. Session은 특별한것이 아니라 단지 Cookie를 사용하여 클라이언트가 아닌
    서버에서 데이터를 저장해두는 방법
  3. Servlet은 Session을 자체적으로 지원

Servlet의 HttpSession

  • Servlet이 공식적으로 지원하는 Session인 HttpSession은 Session 구현에 필요한 다양한 기능들을 지원
  • Servlet을 통해 HttpSession 을 생성하게되면 SessionId가 JSESSIONID로 생성되고 JSESSIONID의 Value는 예측 불가능한 랜덤값으로 생성

ex)

  • request.getSession(true);
    • Default 설정
    • Request 객체 내에 Session이 존재한다면 기존 Session을 반환
    • Request 객체 내에 Session이 없으면 새로운 Session을 생성해서 반환
  • request.getSession(false);
    • Request 객체 내에 Session이 존재한다면 기존 Session을 반환
    • Request 객체 내에 Session이 없으면 null을 반환
  • session.setAttribute(Const.LOGIN_USER, responseDto);
    • Session에 Data를 저장하는 방법으로 request.setAttribute(); 와 비슷
    • 하나의 Session에 여러개의 데이터를 메모리에 저장할 수 있음

Session 2강

Spring의 Session

Spring에서는 Session을 쉽게 다루도록 @SessionAttribute라는 어노테이션이 제공

  • @SessionAttribute
    • request.getSession(true); 와는 다르게 Session을 새로 생성하는 기능X
    • 이미 로그인이 완료된 사용자를 찾는 경우 즉, Session이 있는 경우에 사용

Session 정보

HttpSession은 Session을 간편하게 사용할 수 있도록 다양한 기능을 지원

  1. session.getId();
    • jsessionId 값을 조회할 수 있음
  2. session.getMaxInactiveInterval();
    • 세션의 유효시간
    • second 단위 default는 30분(1800초)
  3. session.getCreationTime();
    • 세션 생성시간

      ex) Sat Dec 9 15:40:23 KST 2024

  4. session.getLastAccessedTime();
    • 해당 세션에 마지막으로 접근한 시간

      ex) Sat Dec 9 15:40:23 KST 2024

  5. session.isNew();
    • 새로 생성된 세션인지 여부

Session 3강

Session TimeOut

Session은 logout 기능을 사용하여 session.invalidate(); 가 되어야 삭제되지만 대부분의 사용자들은 로그아웃을 굳이 하지않고, 브라우저를 종료

Session의 문제점

  • HTTP는 Connectionless 특성을 가지고 있어서 서버가 브라우저 종료 여부 판별X
  • 서버에서 Session을 언제 삭제해야 하는지 판단하기 힘듬
  • JSESSIONID의 값을 탈취 당한 경우 해당 값으로 악의적인 요청을 할 수 있음
  • 세션은 서버 메모리에 생성되고 자원은 한정적이기 때문에 꼭 필요한 경우만 생성해야 함

Session 생명주기

  • 기본적으로 30분을 기준으로 세션을 삭제
  • 실제 로그인 후 30분 이상의 시간동안 사용중인 사용자의 세션 또한 삭제
  • 다시 로그인 해야하는 경우가 발생

HttpSession 사용

  • 세션 생성시점 30분이 아닌 서버에 최근 Session을 요청한 시간을 기준으로 30분을 유지
  • HttpSession은 기본적으로 해당 방식으로 세션의 생명주기를 관리
  • Session 정보에서 LastAccessedTime 을 기준으로 30분이 지나면 WAS가 내부적으로 세션을 삭제

Session의 한계

Session은 서버의 메모리를 사용하여 확장성이 제한

  • 서버가 DB 혹은 메모리에 저장된 세션 정보를 매번 조회하여 오버헤드가 발생
  • 서버가 상태를 유지해야 하므로 사용자 수가 많아질수록 부담이 커짐
  • Cookie는 웹 브라우저에만 존재하여 모바일 앱 등의 다양한 클라이언트에서 인증을 처리할 수 없음
  • Scale Out(수평적 확장)에서 서버간 세션 공유 어려움

오버헤드(Overhead)란?
어떤 처리를 하기 위해 들어가는 간접적인 처리 시간, 메모리 등을 의미


Token

  • Web Application이나 API에서 인증과 인가 과정에서 사용
  • 사용자 또는 시스템의 신원과 권한을 증명하고 요청의 유효성을 검증하는 데 사용되는 디지털 문자열

Token을 사용하는 이유

  1. Token은 서버가 아닌 클라이언트에 저장되어 서버의 부담을 덜 수 있음
  2. Cookie는 웹 브라우저에만 존재하여 모바일 앱 등의 다양한 클라이언트에서 인증을 처리할 수 없음
  3. Token 방식은 Stateless를 기반으로 하여 확장성이 높음
  4. 인증된 사용자임을 확인하기 위한 고유한 서명을 포함하여 위조된 요청인지 확인할 수 있음

Token 동작순서

Token의 단점

  1. Cookie/Session 방식보다 Token 자체의 데이터 용량이 많음
    • 요청이 많아지면 그만큼 트래픽이 증가
  2. Payload(전송되는 데이터)는 암호화되지 않아서 중요한 데이터를 담을 수 없음
  3. Token을 탈취당하면 대처하기 어려워 만료 시간(30분)을 설정

JWT(JSON Web Token)

  • 인증에 필요한 정보들을 암호화시킨 JSON 형태의 Token을 의미
  • JSON 데이터 포맷을 사용하여 정보를 효율적으로 저장하고 암호화로 서버의 보안성을 높임

JWT 구조

XXXXXX.YYYYYY.ZZZZZZ
(Header).(Payload).(Signature) 
  1. Header
    - 토큰의 타입과 해싱 알고리즘을 정의
    ex)
    json
         {
         	"alg": "HS256",
         	"typ": "JWT"
         }
        
  2. Payload
    • 실제로 인증과 관련된 데이터(Claims)를 담고 있음
    • Claims의 종류
      • Registered Claims : 미리 정의된 Claims
        • iss(issuer) : 발행자
        • exp(expiration time) : 만료시간
        • sub(subject) : 제목
        • iat(issued At) : 발행 시간
        • jti(JWT ID) : 토큰의 고유 식별자
      • Public Claims : 사용자가 정의할 수 있는 클레임, 공개용 정보 전달 목적
      • Private Claims : 사용자 지정 클레임, 당사자들 간에 정보를 공유하기 위한 목적

ex)

json
      {
        "sub": "1234567890",
        "name": "Sparta",
        "exp": 1682563600
      }
  1. Signature
    • Header와 Payload를 서버의 Secret Key로 서명하여 암호화
    • 암호화는 Header에서 정의한 알고리즘(alg)을 활용
    • 서명을 통해 서버는 Token이 변조되지 않았음을 확인할 수 있음

ex)

     HMACSHA256(
       base64UrlEncode(header) + "." +
       base64UrlEncode(payload),
       secret
     )

✔base64UrlEncode는 값을 URL에서 사용할 수 있도록 +, /를 각각 -, _로 표기

✔Header와 Payload는 Encoding된 값이기 때문에 복호화 혹은 값을 수정할 수 있지만 Signature는 서버에서 관리하는 값이기 때문에 Secret Key가 유출되지 않는 이상 복호화X


JWT

JWT 인증

  • JWT는 Base64로 인코딩되어 쉽게 복호화 할 수 있음
  • Payload가 그대로 노출되기 때문에 비밀번호나 민감한 정보를 저장하지 않음

JWT 인증 과정

  1. 클라이언트의 로그인 요청
  2. 로그인에 성공했다면 Header, Payload에 Secret Key를 사용하여 Signature를 만듬
    • 이후 Base64로 Encoding
    • 일반적으로 Cookie에 담아 클라이언트에게 JWT를 발급
  3. 발급받은 JWT를 저장 후 서버에 요청할 때 Authorization Header에 JWT를 담아 보냄
  4. 서버에서 JWT의 유효성 검사를 통해 통과한다면 인증에 성공하여 요청을 처리
    • JWT 만료, 위변조 여부를 검사
  • JWT의 유효성 검사

  • 임의로 조작된 데이터를 판별O

✔JSON Web Token의 목적은 정보 보호가 아닌, 위조 방지에 있음

JWT 장단점

JWT 장점

  1. Signature로 서버의 보안성이 증가
  2. Token 자체가 필요한 정보(유저 및 검증 정보)들을 모두 가지고 있음
  3. 서버는 인증 정보와 관련된 별도의 저장소를 사용X
  4. 서버의 수평 확장성(Scale Out)이 높아짐
  5. Cookie가 없는 다른 환경에서도 인증/인가를 적용할 수 있음
  6. DB를 조회하지 않아도 됨

✔Mobile의 경우 App을 자주 닫거나 백그라운드로 전환하여 Session 방식을 사용X

JWT 단점

  1. Payload는 암호화 된 것이 아니라 민감한 정보를 다루지 못함
  2. Token의 길이가 길어서 트래픽이 증가하면 네트워크에 부하가 증가
  3. 클라이언트 측에서 Token을 관리하기 때문에 탈취당하면 대처하기 어려움

Access Token, Refresh Token

  • Token은 클라이언트에서 관리하여 탈취당할 위험성이 높기 때문에 만료 시간 설정이 필요
  • 이 때 발생하는 단점을 극복하기 위해 Access Token과 Refresh Token을 사용

Token의 유형

  1. Access Token
    • 사용자 인증 후 서버가 발급하는 유저 정보가 담긴 토큰
    • 유효 기간 동안 API나 리소스에 접근할 때 사용
  2. Refresh Token
    • Access Token은 보안을 위해 짧은 수명을 가짐
    • Access Token이 만료된 경우 재발급 받기위해 사용
    • 주로 데이터베이스에 유저 정보와 같이 저장

Access Token, Refresh Token 인증

  1. 클라이언트의 로그인 요청
  2. 로그인에 성공했다면 Header, Payload에 Secret Key를 사용하여 Signature를 만듬
  3. 발급받은 JWT를 저장 후 서버에 요청할 때 Authorization Header에 JWT를 담아 보냄
  4. 서버에서 JWT의 유효성 검사를 통해 통과한다면 인증에 성공하여 요청을 처리
  5. Access Token이 만료 되었다면 Refresh Token 으로 토큰 재발급을 요청
  6. 서버로부터 Access Token을 재발급

Filter 1강

공통 관심 사항

같은 말로 횡단 관심사 라고 하며 여러 위치에서 공통적으로 사용되는 부가 기능이고 Filter가 나오게 된 이유는 공통 관심사의 처리 때문

Servlet Filter

Servlet Filter는 보안, 로깅, 인코딩, 인증/인가 등 다양한 작업을 처리하기 위해 사용

Servlet Filter 특징

  1. 공통 관심사 로직 처리
    • 공통된 로직을 중앙 집중적으로 구현하여 재사용성이 높고 유지보수가 쉬움
    • 모든 요청이 하나의 입구를 통해 처리되어 일관성을 유지
  2. HTTP 요청 및 응답 필터링
  3. Filter Chain
    • 여러 개의 필터가 순차적으로 적용될 수 있음
    • filterChain.doFilter(request, response); 다음 필터로 제어를 전달
  4. doFilter()
    • 실제 필터링 작업을 수행하는 주요 메소드로 필터가 처리할 작업을 정의
    • 다음 필터로 제어를 넘길지 여부를 결정

Servlet Filter 적용

  • Filter를 적용하면 Servlet이 호출되기 이전에 Filter를 항상 거치게 됨
  • 공통 관심사를 필터에만 적용하면 모든 요청 or 응답에 적용
  • Filter는 특정 URL Pattern에 적용할 수 있음
  • Spring을 사용하는 경우 Servlet은 Dispatcher Servlet

인증/인가 필터 동작

  • 로그인 성공

    • 정상적으로 Controller를 호출
  • 로그인 실패

    • Filter에서 적절하지 않은 요청이면 Controller를 호출X
  • Filter Chain

  • Filter는 Chain 형식으로 구성
  • Filter는 개발자가 자유롭게 추가할 수 있음
  • Filter는 순서를 지정하여 추가할 수 있음

Filter 2강

Filter Interface

Java Servlet에서 HTTP 요청과 응답을 가로채고, 이를 기반으로 다양한 처리 작업을 수행하는 데 사용

jakarta.servlet.Filter

  • Filter Interface를 Implements하여 구현하고 Bean으로 등록하여 사용
    • Servlet Container가 Filter를 Singleton 객체로 생성 및 관리

주요 메서드

  1. init()
    • Filter를 초기화하는 메서드
    • Servlet Container가 생성될 때 호출
    • default method이기 때문에 implements 후 구현하지 않아도 됨
  2. doFilter()
    • Client에서 요청이 올 때 마다 doFilter() 메서드가 호출
      • doFilter() 내부에 필터 로직(공통 관심사 로직)을 구현
    • WAS에서 doFilter() 를 호출해주고 하나의 필터의 doFilter()가 통과된다면 Filter Chain에 따라서 순서대로 doFilter() 를 호출
    • 더이상 doFilter() 를 호출할 Filter가 없으면 Servlet이 호출
  3. destroy()
    • 필터를 종료하는 메서드
    • Servlet Container가 종료될 때 호출
    • default method이기 때문에 implements 후 구현하지 않아도 된다.

Servlet Filter 구현

Filter 구현체

  • 요청 URL을 Log로 출력하는 Filter
    @Slf4j
    public class CustomFilter implements Filter {
        @Override
        public void doFilter(
                ServletRequest request,
                ServletResponse response,
                FilterChain chain
        ) throws IOException, ServletException {
    
            // Filter에서 수행할 Logic
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            String requestURI = httpRequest.getRequestURI();
    
            log.info("request URI={}", requestURI);
            // chain 이 없으면 Servlet을 바로 호출
            chain.doFilter(request, response);
        }
    }
    • doFilter() 는 더 이상 호출할 Filter가 없다면 Servlet을 호출
    ServletRequest 는 기능이 별로 없어서 대부분 기능이 많은 HttpServletRequest 를 다운 캐스팅 하여 사용

Filter 등록

  @Configuration
  public class WebConfig implements WebMvcConfigurer {
  		
  		@Bean
  		public FilterRegistrationBean customFilter() {
  				FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
  				// Filter 등록
  				filterRegistrationBean.setFilter(new CustomFilter());
  				// Filter 순서 설정
  				filterRegistrationBean.setOrder(1);
  				// 전체 URL에 Filter 적용
  				filterRegistrationBean.addUrlPatterns("/*");
  				
  				return filterRegistrationBean;
  		}
  }
  • setFilter()
    • 등록할 필터를 파라미터로 전달
  • setOrder()
    • Filter는 Chain 형태로 동작
    • 즉, 실행될 Filter들의 순서가 필요
    • 파라미터로 전달될 숫자에 따라 우선순위가 정해짐
    • 숫자가 낮을수록 우선순위가 높음
  • addUrlPatterns()
    • 필터를 적용할 URL 패턴을 지정
    • 여러개 URL 패턴을 한번에 지정할 수 있음
    • 규칙은 Servlet URL Pattern과 같음
  • filterRegistrationBean.addUrlPatterns("/*")
  • Controller보다 앞에서 Filter가 동작

느낀 점

여유롭게 하지 않고 타이트하게 하려고 해서 그런지 잠을 충분히 자도 금방 피곤해지는 것 같다. 머리를 너무 많이 써서 그런가? 나를 최대한으로 써야 이정도 속도인데 조금이라도 여유를 부리면 어떻게 될지.. 😭 집에 있는데 집 가고 싶네.. 어차피 인생에 있어서 지금 몇 개월은 찰나의 순간이니까 어떻게든 해보자 화이팅😤

profile
걱정하지 말고 일단 해봐!

0개의 댓글