[주요정보통신기반시설 웹 애플리케이션 취약점 분석·평가 21일 완성 코스] 18강 — [Day 18] 쿠키 변조

⚡ 정다은 · 2026-10-08 14:00 · 조회 23

안녕하세요! Hackerstory 공부방의 IT/보안 튜터 정다은입니다. 👋

어느덧 [주요정보통신기반시설 웹 애플리케이션 취약점 분석·평가 21일 완성 코스]의 18일 차 강의에 도달했습니다! 지난 시간에 배운 '데이터 평문 전송(Day 17)'에 이어, 오늘은 클라이언트 측에 저장되는 데이터의 허점을 노리는 아주 고전적이면서도 치명적인 취약점인 **'쿠키 변조(Cookie Tampering)'**에 대해 알아보겠습니다.

HTTP 프로토콜의 특성상 클라이언트(브라우저)에서 서버로 보내는 모든 요청은 사용자가 임의로 조작할 수 있다는 사실, 기억하시죠? 개발자가 이 점을 간과하고 중요 정보를 쿠키에 담아두면 어떤 일이 발생하는지, 지금부터 자세히 파헤쳐 봅시다!


[Day 18] 쿠키 변조 (Cookie Tampering)


1. 개념 설명: 쿠키 변조란 무엇인가요?

💡 쿠키(Cookie)의 기본 개념

웹 프로토콜인 HTTP는 **Stateful(상태 유지)**이 아니라 Stateless(비상태성) 프로토콜입니다. 즉, 이전 요청과 다음 요청이 서로 연결되지 않습니다. 그래서 웹 사이트는 로그인 상태나 장바구니 목록 등을 기억하기 위해 **쿠키(Cookie)**라는 작은 텍스트 조각을 클라이언트 브라우저에 저장하고, 서버에 요청을 보낼 때마다 이 쿠키를 함께 전송합니다.

⚠️ 쿠키 변조 취약점의 원리

쿠키 변조 취약점은 서버가 클라이언트 브라우저에 저장된 쿠키 값을 **‘무조건 신뢰’**할 때 발생합니다.

웹 브라우저의 개발자 도구(F12)나 프록시 툴(Burp Suite 등)을 사용하면 클라이언트에 저장된 쿠키 값을 사용자가 눈으로 확인하고, 원하는 값으로 자유롭게 수정할 수 있습니다. 만약 웹 애플리케이션이 쿠키에 사용자의 등급, 권한, 결제 금액, 사용자 ID 등을 평문( 또는 쉽게 복호화할 수 있는 형태로) 저장하고 이를 검증 없이 신뢰한다면, 공격자는 단 몇 초 만에 다른 사람의 계정으로 위장하거나 관리자 권한을 획득할 수 있게 됩니다.

🎟️ 비유하자면? 놀이공원에서 "VIP 손님"이라고 직접 수기로 적을 수 있는 종이 자유이용권을 발급해 준 것과 같습니다. 손님이 볼펜으로 '일반'을 'VIP'로 고쳐 써서 제출하는데, 입구의 검표원이 중앙 시스템 확인 없이 종이에 적힌 글자만 보고 VIP 라인으로 통과시켜 주는 상황인 것이죠!


2. 실무 예시 및 공격 시나리오

쿠키 변조 공격이 실제로 어떻게 일어나는지 구체적인 시나리오를 통해 살펴보겠습니다.

🎭 시나리오 1: 권한 우회 및 관리자 승격 (Privilege Escalation)

서비스 개발자가 로그인 성공 시 아래와 같이 쿠키를 생성하여 브라우저에 전달했다고 가정해 봅시다.

[서버가 전달한 정상 쿠키 응답]

HTTP/1.1 200 OK
Set-Cookie: user_id=kim123; user_role=USER; is_admin=false

공격자 kim123은 브라우저의 개발자 도구를 열어 쿠키 값을 다음과 같이 변경합니다.

[공격자가 변조하여 전송한 쿠키 요청]

GET /admin/dashboard HTTP/1.1
Host: vulnerable-bank.com
Cookie: user_id=kim123; user_role=ADMIN; is_admin=true

서버가 세션 DB나 토큰 검증 없이 is_admin=true라는 쿠키 값만 확인하고 관리자 페이지 접근을 허용한다면, 공격자는 클릭 한 번으로 관리자 권한을 탈취하게 됩니다.


💳 시나리오 2: 비즈니스 로직(금액) 변조

장바구니 시스템에서 결제할 상품 정보와 금액을 쿠키에 담아 처리하는 취약한 쇼핑몰이 있습니다.

[정상적인 결제 요청 쿠키]

GET /checkout HTTP/1.1
Host: vulnerable-shop.com
Cookie: item_id=LAPTOP_01; price=1500000; qty=1

공격자는 Burp Suite 등의 프록시 도구를 이용해 요청을 가로챈 후 price 값을 수정합니다.

[변조된 결제 요청 쿠키]

GET /checkout HTTP/1.1
Host: vulnerable-shop.com
Cookie: item_id=LAPTOP_01; price=100; qty=1

서버가 결제 금액을 DB의 실제 상품 가격과 비교하지 않고 쿠키에 담긴 price 값으로 PG사 결제를 요청하면, 150만 원짜리 노트북을 100원에 구매하는 치명적인 보안 사고가 발생합니다.


🔍 시나리오 3: 인코딩 및 단순 암호화 착각 (Base64 등)

개발자가 나름대로 보안을 고려하여 쿠키 값을 Base64로 인코딩하여 저장하는 경우가 있습니다.

  • 원래 값: user_id=guest&role=user
  • Base64 인코딩 후 쿠키: dXNlcl9pZD1ndWVzdCZyb2xlPXVzZXI=

공격자는 이를 보고 Base64 디코딩을 수행하여 원문 데이터를 확인합니다. 그 후 user_id=admin&role=super_admin으로 문자열을 고친 뒤, 다시 Base64로 인코딩(dXNlcl9pZD1hZG1pbiZyb2xlPXN1cGVyX2FkbWlu)하여 쿠키로 전송합니다.

⚠️ 주의: Base64나 Hex(16진수) 변환은 **'암호화'가 아닌 '인코딩(단순 형태 변환)'**일 뿐입니다. 누구나 역으로 계산해 원본을 알아낼 수 있으므로 보안 대책이 될 수 없습니다!


3. 보안 대책 및 대응 방안 (Remediation)

쿠키 변조 취약점을 근본적으로 해결하기 위해서는 **"클라이언트에 저장되는 모든 데이터는 조작될 수 있다"**는 원칙에서 출발해야 합니다.

1) 민감 정보의 쿠키 직접 저장 금지 & 서버 측 세션(Session) 사용

권한, 사용자 식별 정보, 결제 정보 등 보안상 중요한 데이터는 절대 클라이언트 쿠키에 직접 저장하면 안 됩니다. 대신 서버 측 세션(Server-side Session) 메커니즘을 사용해야 합니다.

  • 올바른 구조:
    1. 서버는 예측 불가능한 무작위 문자열인 **세션 ID(Session ID)**만 생성하여 쿠키로 클라이언트에 전달합니다.
    2. 사용자의 식별자, 권한, 상태 등 민감한 정보는 서버의 메모리나 DB 세션 스토어에 보관합니다.
    3. 클라이언트가 세션 ID를 보낼 때마다 서버가 내부 DB에서 권한을 조회하여 검증합니다.
[취약한 구조]
클라이언트 (Cookie: role=admin) ---> 서버 ("role이 admin이네? 통과!")

[안전한 구조]
클라이언트 (Cookie: JSESSIONID=a8f9c2...) ---> 서버 (세션 DB 조회: "a8f9c2는 일반 유저(USER)군. 통과 불가능!")

2) 쿠키 서명(Signing) 및 암호화(Encryption) 적용

어쩔 수 없이 클라이언트 측 쿠키에 데이터를 저장해야 하는 구조라면(예: Stateless한 JWT 또는 쿠키 기반 세션), 서버만 알고 있는 비밀키(Secret Key)로 서명하거나 암호화해야 합니다.

  • 서명(Digital Signature): 쿠키 내용 뒤에 HMAC 형태의 서명 값을 붙여 전송합니다. 클라이언트가 데이터를 1비트라도 수정하면 서버에서 서명 검증이 실패하여 조작 여부를 즉시 감지할 수 있습니다.

3) 쿠키 보안 속성(Security Flags) 필수 설정

쿠키를 생성할 때는 변조 외에도 탈취 방지를 위해 아래의 3가지 핵심 보안 플래그를 반드시 설정해야 합니다.

  • HttpOnly: 자바스크립트(document.cookie)를 통한 쿠키 접근을 차단합니다. (XSS 공격을 통한 세션 쿠키 탈취 방지)
  • Secure: HTTPS(암호화 통신) 연결일 때만 쿠키를 전송하도록 설정합니다. (Day 17에서 배운 데이터 평문 전송 및 도청 방지)
  • SameSite=Strict 또는 Lax: 타 사이트에서의 요청 시 쿠키 전송을 제한하여 CSRF 공격을 방지합니다.

🛠️ 보안 설정 예시 코드

[Java / Spring Boot 예시]

// 취약한 예시: 민감한 정보를 평문 쿠키로 저장
Cookie roleCookie = new Cookie("user_role", "ADMIN");
response.addCookie(roleCookie);

// 안전한 예시: 세션 사용 및 쿠키 보안 플래그 설정
ResponseCookie cookie = ResponseCookie.from("SESSIONID", request.getSession().getId())
        .httpOnly(true)     // JS 접근 불가
        .secure(true)       // HTTPS 전용
        .path("/")
        .maxAge(3600)
        .sameSite("Strict") // CSRF 방지
        .build();

response.setHeader(HttpHeaders.SET_COOKIE, cookie.toString());

[Node.js / Express 예시]

// express-session 활용을 통한 안전한 세션 쿠키 설정
const session = require('express-session');

app.use(session({
  secret: 'my-super-secret-key-1234', // 세션 암호화 키
  resave: false,
  saveUninitialized: false,
  cookie: {
    httpOnly: true, // XSS 방지
    secure: true,   // HTTPS 전용
    sameSite: 'strict',
    maxAge: 3600000 // 1시간
  }
}));

4. 요약 및 정리

오늘 배운 내용을 한 줄로 요약하자면 "클라이언트가 보내오는 쿠키 값은 절대 믿지 말고, 중요 상태는 서버에서 직접 관리하라" 입니다.

구분 취약한 방식 ❌ 안전한 보안 방식 ⭕
데이터 저장 위치 클라이언트 쿠키에 권한/금액/ID 직접 저장 서버 측 세션 스토어(DB/Redis)에 저장
쿠키 역할 사용자 상태 데이터 자체를 전달 난수화된 세션 식별자(Session ID)만 전달
검증 방식 쿠키 파라미터 값 단순 확인 서버 DB 조회 및 무결성 서명 검증
보안 옵션 플래그 미설정 (Set-Cookie: sid=123) HttpOnly, Secure, SameSite 플래그 적용

웹 보안 진단을 수행할 때 쿠키에 role, auth, admin, price, user_id 같은 이름의 파라미터가 들어있는지 유심히 관찰해 보세요. 만약 발견된다면 해당 값을 변경했을 때 권한이 우회되는지 검증하는 것이 정석적인 진단 모범 사례입니다!


📍 다음 강의 예고: [Day 19] 관리자 페이지 노출

고생 많으셨습니다! 이제 커리큘럼도 막바지를 향해 달려가고 있네요.

다음 시간 Day 19에서는 '관리자 페이지 노출' 취약점을 다룹니다. 웹 서비스의 최고 권한이 모여있는 관리자 페이지가 추측하기 쉬운 URL, 브라우저 로컬 스토리지 leakage, 혹은 구글링(구글 해킹) 등을 통해 외부 공격자에게 노출될 때 발생하는 위협과 이를 철저히 숨기고 보호하는 보안 대책을 공부하겠습니다.

그럼, 다음 시간에도 즐겁게 열공해 봅시다! 궁금한 점은 언제든 댓글로 남겨주세요. 😊

댓글 0

로그인하면 댓글을 작성할 수 있습니다.