[FRONT] XSS와 CSRF

XSS와 CSRF에 대해 이해해 보자

Posted by lim.Chuck on December 26, 2024

[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 헤더 이해하기

웹 보안의 기본: XSS와 CSRF 최종정리 🔒

1. XSS (Cross-Site Scripting) 란?

해커가 웹사이트에 악성 스크립트를 심어 다른 사용자를 공격하는 방법

  • CSS와 이름이 겹쳐서 Cross(X)와 Site Scripting(SS)를 따와 XSS로 명명

XSS 공격 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1. 해커의 악성 댓글
const 악성댓글 = `
    상품 좋아요!
    <script>
        // 사용자 정보를 해커에게 전송하는 코드
        해커사이트로전송(document.cookie);
    </script>
`;

// 2. 위험한 처리 방식 (❌)
댓글영역.innerHTML = 악성댓글; // 악성 스크립트 실행됨!

// 3. 안전한 처리 방식 (✅)
댓글영역.textContent = 악성댓글; // 텍스트로만 표시

2. CSRF (Cross-Site Request Forgery) 란?

해커가 사용자를 속여서 의도치 않은 동작을 하게 만드는 공격

CSRF 공격 예시

1
2
3
4
5
6
7
8
9
10
11
// 1. 정상적인 송금 기능
function 일반송금(계좌, 금액) {
  fetch("/api/송금", {
    method: "POST",
    body: JSON.stringify({ 계좌, 금액 }),
  });
}

// 2. 해커의 피싱 사이트
// "당신의 고양이가 너무 귀여워요!" 클릭시 실행되는 코드
자동송금("해커계좌", "1000000"); // 사용자도 모르게 송금됨!

3. 방어 방법 🛡️

1) XSS 방어

1
2
3
4
5
6
7
8
9
10
11
12
// 1. textContent 사용하기
element.textContent = 사용자입력;

// 2. 특수문자 치환하기
function 안전한텍스트만들기(위험한텍스트) {
  return 위험한텍스트
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#039;");
}

2) CSRF 방어

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 1. 안전한 API 요청
async function 안전한송금(계좌, 금액) {
  const 보안토큰 = document.querySelector('meta[name="csrf-token"]').content;

  await fetch("/api/송금", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "CSRF-Token": 보안토큰, // 보안 토큰 추가
    },
    credentials: "include", // 쿠키 포함
    body: JSON.stringify({
      계좌: 계좌,
      금액: 금액,
    }),
  });
}

4. 실제 적용 예시

1) 게시판 댓글 시스템

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class 안전한댓글시스템 {
  // 댓글 표시하기
  댓글표시(댓글내용) {
    // XSS 방어: innerHTML 대신 textContent 사용
    댓글영역.textContent = 댓글내용;
  }

  // 댓글 작성하기
  async 댓글작성(내용) {
    const 보안토큰 = this.보안토큰가져오기();

    // CSRF 방어: 보안 토큰 포함
    await fetch("/api/댓글작성", {
      method: "POST",
      headers: {
        "CSRF-Token": 보안토큰,
      },
      body: JSON.stringify({ 내용 }),
    });
  }
}

5. 핵심 정리 ✨

  1. XSS 기억하기

    • 사용자 입력을 그대로 HTML에 넣지 않기
    • innerHTML 대신 textContent 사용하기
    • 필요하면 특수문자 치환하기
  2. CSRF 기억하기

    • 중요한 동작에는 보안 토큰 사용하기
    • 토큰 검증 없이 중요 동작 실행하지 않기
    • 로그인 상태 확인하기
  3. 일반적인 보안 수칙

    • 사용자 입력은 항상 위험하다고 생각하기
    • 중요한 동작은 반드시 검증하기
    • 보안은 여러 단계로 구현하기