본문 바로가기

Spring Boot/유저 관리 기능

쿠키와 세션 기반 인증 : 쿠키와 세션 보안 설정

- 쿠키와 세션에서 사용되는 보안 속성(Secure, HttpOnly, SameSite)의 의미, 적용법

- 각 속성이 방어하는 공격 벡터(XSS, 중간자 공격, CSRF)를 구분하여 설명하기

- 실무에서 사용하는 쿠키 발급 규칙과 검증 체크리스트

- CSRF 방어전략(동일 사이트 정책, 동시 토큰, 동기화 토큰)을 워크플로우 + 코드로 구현하기

- 배포 환경(개발/스테이징/운영)에 따른 설정 프로파일 설계


Secure 설정의 이해와 적용

Secure 속성은 암호화된 연결(HTTPS)에서만 쿠키가 전송되도록 제한한다고 함

네트워크 구간에서 쿠키가 노출될 위험(중간자 공격, 스니핑)을 크게 줄여준다

 

1. 개념

의미 : 요청이 HTTPS가 아닐 경우, 쿠키가 전송되지 않는다

효과 : 중간자 공격에 의해 인증 쿠키가 탈취될 위험을 완화함

전제 : 서비스 전체가 HTTPS를 사용해야함. 운영 환경에서는 HSTS(Strict-Transport-Security)도 함께 고려하기

 

 

(1) HSTS(HTTP Strict Transport Security)

개념 : 클라이언트(브라우저)에게 "이 도메인은 무조건 HTTPS로만 접속해야한다"는 정책을 전달하는 HTTP 응답 헤더임

예시:

Strict-Transport-Security: max-age=31536000;
includeSubDomains;
preload

 

max-age=31536000; : 1년동안 HTTPS를 허용하기

includesubDomains; : 모든 서브도메인에 적용하기

preload : 주요 브라우저 벤더의 HSTS Preload 리스트에 등록하기

 

적용 효과

 - HTTP 요청이 발생해도 브라우저가 자동으로 HTTPS로 바꿔서 요청해줌

 - 중간자 공격(MITM)이나 다운그레이드 공격을 방어함

 

 

2. 적용 예시(서버가 쿠키를 발급함)

HTTP/1.1 200 OK
Set-Cookie: SESSION=abc123;
Path=/;
HttpOnly;
SameSite=Lax;
Max-Age=1800

 

// 프레임워크를 사용하지 않고 쿠키 문자열을 생성하는 러프한 예
String sessionId = UUID.randomUUID().toString();
String cookie = "SESSION=" + sessionId // 세션ID
	+ "; Path=/" // 전체 경로에 적용
    + "; Secure" // HTTPS에서만 전송함
    + "; HttpOnly" // JS 접근 차단
    + "; SameSite=Lax" // CSRF 완화
    + "; Max-Age=1800"; // 30분
    
response.addHeader("Set-Cookie", cookie);

 

 

3. 워크플로우(Secure 동작)

브라우저가 서버에게 요청보냈을 때 서버가 헤더에 보내주는 쿠키

 

 

* 운영 환경에서는 HTTPS 강제 리다이렉트와 HSTS를 함께 적용해야함

* 개발 환경에서는 로컬 인증서(mkcert)를 활용해서 HTTPS를 손쉽게 구성할 수 있다

* 서브도메인간 서비스가 섞여있다면, 모든 도메인이 HTTPS를 사용하도록 일괄 적용할 수 있음


HttpOnly 설정의 이해와 적용

HttpOnly는 브라우저 자바스크립트에서 쿠키에 대한 접근을 차단한다.

XSS로부터 쿠키 읽기를 어렵게 만들어서 세션 탈취를 완화할 수 있음

 

1. 개념

의미 : document.cookie로 쿠키 값을 읽을 수 없음

효과 : 악성 스크립트가 쿠키를 훔쳐가는 것을 차단함

주의 : XSS 자체를 없애진 않는다. 단지 쿠키 탈취를 막아줄 뿐이며, DOM 조작 등은 여전히 가능함

 

 

2. 적용 예

Set-Cookie: SESSION=abc123; 
	Path=/;
    HttpOnly;
    Secure;
    SameSite=Lax;
    Max-Age=1800

 

// 쿠키를 HttpOnly로 발급함
String cookie = "SESSION=" + sessionId  + "; Psth=/; HttpOnly; Secure; SameSite=Lax; Max-Age=1800";
response.addHeader("Set-Cookie", cookie); // 위의 쿠키모양이 됨

 

 

3. 워크플로우(HttpOnly 효과)

쿠키에 HttpOnly; 를 넣으면 JS 입력을 통해 쿠키를 찾을 수 없다

 

 

* 인증에 쓰는 모든 쿠키는 기본값으로 HttpOnly를 사용한다

* SPA에서도 인증 토큰을 localStorage에 저장하지 말고, 가능하면 HttpOnly 쿠키를 사용하기

* CSP(Content-Security-Policy), 출력 인코딩 등 XSS 방어도 함께 적용하기

 

 

정리

항목 요약
목적 JS 접근 차단으로 쿠키 탈취 완화하기
한계 XSS 자체는 차단하지 않음
권장 인증 쿠키는 기본적으로 HttpOnly

SameSite 설정의 이해와 적용

SameSite는 다른 사이트에서 오는 요청에 쿠키가 자동으로 첨부되는지 제어하여 CSRF를 완화한다

 

 

1. 옵션 비교

옵션 동작 요약 대표 용도 주의사항
Strict 크로스 사이트 모든 내비게이션에서 쿠키를 미전송함 민감한 백오피스 소셜 로그인, 외부 리디렉션시 불편함
Lax 대부분의 크로스 사이트 요청에서 미전송, 탑 레벨 GET 요청 네비게이션은 예외 일반 웹앱의 기본값임 폼 POST, XHR, iframe 요청은 미전송함
None 출처에 상관없이 전송함 제3자의 쿠키가 필요함
(SSO, 임베드)
반드시 Secure을 동반함

 

 

2. 적용 예

Set-Cookie: SESSION=abc123;
	Path=/;
    Secure;
    HttpOnly;
    SameSite=Lax

 

// 서비스 기본 전략 : Lax
String cookie = "SESSION=" + sessionId + "Path=/; Secure; HttpOnly; SameSite=Lax"; // Lax
response.addHeader("Set-Cookie", cookie);

 

 

3. 워크플로우(SameSite 효과)

SameSite=Lax 워크플로우

 

 

* 기본은 Lax 권장

* 결제 리다이렉트, 소셜 로그인등 크로스 사이트 상호작용이 필수인 경로에 한해 None; Secure를 제한적으로 사용함

* 하위 도메인, 외부 도메인 연동시 정확한 범위 분석을 문서화함

 

 

정리 SameSite

항목 요약
목적 크로스 사이트 자동 쿠키 전송 제한으로 CSRF 완화
기본값 Lax를 권장함
특수 케이스 SSO, 결제는 None; Secure 검토

 


CSRF 공격 예방을 위한 설정과 구현

SameSite만으로 충분하지 않은 경우가 있을 수 있다.

토큰 기반 방어를 함께 적용해야함

 

1. CSRF 공격

  • 정의 : CSRF(Cross-Site Request Forgery)는 사용자가 자신도 모르게 공격자가 의도한 요청을 특정 웹 애플리케이션에 보내도록 속이는 공격
  • 원리 : 피해자가 로그인하여 인증 쿠키를 보유중일 때, 공격자가 조작된 링크나 폼을 열게 하면 브라우저가 자동으로 쿠키를 첨부하여 서버에 요청을 보냄
  • 예시
    • 사용자가 은행 사이트에 로그인하여 세션 쿠키를 보유함
    • 공격자가 이메일에 악성 링크(<img src = "https://bank.com/transfer?to=attacker&amount=1000">)를 삽입
    • 사용자가 이메일을 열면 브라우저가 은행 서버에 요청을 보내고, 자동으로 세션 쿠키가 첨부됨
    • 서버는 정상적인 사용자 요청으로 인식하고 이체를 수행한다
  • 결과 : 사용자의 의도와는 다르게 권한이 있는 요청이 실행됨

CSRF 공격

 

 

 

1. 방어 전략

전략 개념 구현 포인트
동기화 토큰(Synchronizer Token) 서버가 세션별 CSRF 토큰을 발급,
폼과 함께 제출함
서버 저장소에 토큰을 보관 후 일치 여부를 검증함
더블 서브밋(Double Submit Cookie) 쿠키로 CSRF 토큰을 별도로 발급, 
본문 / 헤더값과 비교함
서버는 값 비교만, 저장이 불필요함(서명 권장)
SameSite 쿠키 자동 전송 제한 Lax 기본, 특수 경로는 None; Secure

 

 

 

2. 동기화 토큰 워크플로우

CSRF 대응 동기화 토큰 워크플로우

 

 

3. 코드 예(동기화 토큰, 러프한 예)

        // 1. 토큰 발급하기
        byte[] buf = new byte[32];
        new SecureRandom().nextBytes(buf);
        String csrf = Base64.getUrlEncoder().withoutPadding().encodeToString(buf);
        session.setAttribute("CSRF", csrf); // 세션 저장소에 보관

        // 2. 뷰에 숨김필드로 전달하기(서버 렌더 기준)
//        String form = "<form method=\\"POST\\" action=\\"/transfer\\">"+
//                "<input type=\\"hidden\\" name=\\"csrf\\" value=\\""+csrf+"\\">"+
//                "...</form>";
        String form = "<form method=\"POST\" action=\"/transfer\">"+
                "<input type=\"hidden\" name=\"csrf\" value=\""+csrf+"\">"+
                "...</form>";
        
        // 3. 검증하기
        String presented = request.getParameter("csrf");
        String stored = (String)session.getAttribute("CSRF");
        if(stored == null || !stored.equals(presented)) {
            response.setStatus(403); // CSRF 실패
            return;
        }

 

 

4. 더블 서브밋 워크플로우

더블 서브밋 워크플로우

 

 

5. 더블 서브밋 코드 예시(러프)

// 발급하기 : 쿠키와 뷰 모두에 동일한 토큰을 제공함(쿠키는 HttpOnly 미적용, 비교를 위해 읽을 수 있어야함)
String csrf = Base64.getUrlEncoder().withoutPadding().encodeToString(buf);
String csrfCookie = "CSRF_TOKEN=" + csrf + "; Path=/; Secure; SameSite=Lax";
response.addHeader("Set-Cookie", csrfCookie);

// 전송하기 : 프론트는 헤더나 숨길필드로 csrf를 같이 보낸다
String fromCookie = CookieUtil.parse(request.getHeader("Cookie")).get("CSRF_TOKEN");
String fromHeader = request.getHeader("X-CSRF-TOKEN");
boolean ok = fromCookie != null && fromCookie.equals(fromHeader);
if(!ok) {
	response.setStatus(403);
    return;
}

 

* 더블 서브밋의 경우 쿠키값을 클라이언트가 읽어야 하므로 HttpOnly를 적용하지 않는다.

위변조 방지를 위해 서명된 토큰(HMAC)을 사용하면 더욱 안전하다고 한다.

 

 

* 상태 변경요청(POST, PUT, DELETE)에만 CSRF 검증을 수행하고, GET은 안전한 동작의 경우에만 수행한다.

* 프록시/게이트웨이(Nginx, API Gateway) 레벨에서 SameSite 재설정과 헤더 검증을 보조할 수 있다고 한다.

* SSO, 결제 리다이렉트 등에서 SameSite=None; Secure 가 필요한 경로의 경우 화이트리스트로 따로 관리한다.

 

 

정리

항목 요약
동기화 토큰 서버가 보관하고 제출값과 일치하는지 확인
더블 서브밋 쿠키와 헤더/본문값 일치하는지 확인(서명 권장)
SameSite 크로스 사이트 자동 쿠키 전송 제한