동일 출처 정책과 2가지 공격

김현중·2025년 3월 14일

연구소

목록 보기
27/34

동일 출처 정책(Same-Origin Policy): 웹 보안의 첫 방어선

동일 출처 정책은 웹 브라우저에 구현된 가장 기본적인 보안 메커니즘입니다. 이 정책은 한 출처(origin)에서 로드된 문서나 스크립트가 다른 출처의 리소스와 상호작용하는 것을 제한합니다.

여기서 '출처'는 다음 세 가지 요소의 조합으로 정의됩니다.

  1. 프로토콜(http, https)
  2. 도메인(example.com)
  3. 포트 번호(80, 443, 3000)

이 세 가지 요소가 모두 동일해야만 같은 출처로 간주됩니다.

이 정책이 없다면?

사용자가 은행 웹 사이트에 로그인한 상태에서 악의적인 사이트를 열면, 그 사이트의 JavaScript가 은행 사이트의 DOM에 접근하여 계좌 정보를 읽거나 송금 요청을 할 수 있게 됩니다. 동일 출처 정책은 이러한 위험으로 부터 사용자를 보호합니다.


교차 출처 통신 허용하기

때로는 다른 출처와의 통신이 필요한 상황이 있습니다. 이를 위한 안전한 방법들은 다음과 같습니다.

  1. CORS(Cross-Origin Resource Sharing)

서버가 HTTP 헤더를 통해 특정 출처의 접근을 명시적으로 허용할 수 있습니다.

Access-Control-Allow-Origin: https://trusted-site.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization
  1. JSONP(JSON with Padding)

<script> 태그는 동일 출처 정책의 제약을 받지 않는 점을 이용한 기법입니다. 하지만 보안 취약점이 있어 현대 웹에서는 CORS를 사용하는 것이 권장됩니다.

  1. postMessage API

다른 윈도우(iframe, 팝업 등) 간에 안전하게 메시지를 주고 받을 수 있는 메서드입니다.



XSS(Cross-Site Scripting): 악성 코드 주입 공격

XSS는 가장 흔하고 위험한 웹 취약점 중 하나입니다. 공격자가 웹 페이지에 악성 클라이언트 측 코드를 삽입하여 사용자의 브라우저에서 실행되게 하는 공격입니다.

XSS 공격의 유형

1. 저장형(Stored) XSS

악성 스크립트가 서버에 영구적으로 저장되어 해당 페이지를 방문하는 모든 사용자에게 영향을 미칩니다.

예시 시나리오:

  1. 공격자가 블로그 댓글에 <script>악성코드</script>를 포함한 내용 작성
  2. 서버가 이 댓글을 필터링 없이 저장
  3. 다른 사용자가 해당 블로그 글을 볼 때마다 악성 스크립트가 실행됨

2. 반사형(Reflected) XSS

악성 스크립트가 URL 파라미터 등을 통해 웹 애플리케이션에 즉시 반사되어 실행됩니다.

예시 시나리오:

  1. 공격자가 https://example.com/search?q=<script>악성코드</script> 같은 URL 생성
  2. 사용자가 이 링크를 클릭하면 서버가 쿼리 파라미터를 그대로 응답 페이지에 포함
  3. 사용자의 브라우저에서 악성 스크립트가 실행됨

3. DOM 기반 XSS

클라이언트 측 JavaScript가 안전하지 않은 방식으로 DOM을 조작할 때 발생합니다.

예시 코드:

document.getElementById("output").innerHTML = location.hash.substring(1);

이 코드는 URL의 해시 부분을 필터링 없이 DOM에 삽입하므로, website.com/page#<script>악성코드</script>와 같은 URL을 통해 공격이 가능합니다.



XSS 방어 방법

1. 입력 데이터 검증 및 필터링

사용자 입력을 받을 때는 항상 데이터를 검증하고 필터링해야 합니다.

  • 화이트리스트 접근법: 허용된 문자나 패턴만 입력받고 나머지는 거부
  • 특수문자 필터링: <, >, ", ', /, ; 등의 특수문자 필터링
  • 정규식을 통한 패턴 검증

2. 출력 시 HTML 인코딩

사용자 입력 데이터를 출력할 때는 컨텍스트에 맞게 인코딩해야 합니다.

  • HTML 컨텍스트: < → <, > → > 등으로 변환
  • JavaScript 컨텍스트: 문자열 이스케이프 처리
  • URL 컨텍스트: encodeURIComponent() 사용
  • CSS 컨텍스트: CSS 이스케이프 처리
// 안전한 DOM 조작 예시
document.getElementById("output").textContent = userInput; // textContent 사용
// 또는
document.getElementById("output").innerText = userInput; // innerText 사용

3. Content-Security-Policy(CSP) 헤더 사용

CSP는 웹 페이지에서 로드될 수 있는 리소스를 제한하여 XSS 공격의 영향을 줄입니다.

Content-Security-Policy: script-src 'self' https://trusted-cdn.com;

이 헤더는 스크립트가 오직 같은 출처와 trusted-cdn.com에서만 로드될 수 있도록 제한합니다.

4. 쿠키 보안 설정

민감한 쿠키(특히 세션 쿠키)는 다음과 같은 플래그를 설정해야 합니다.

Set-Cookie: SessionId=abc123; HttpOnly; Secure; SameSite=Strict
  • HttpOnly: JavaScript에서 쿠키 접근 불가능
  • Secure: HTTPS 연결에서만 쿠키 전송
  • SameSite: 교차 출처 요청에서 쿠키 전송 제한

5. 현대적인 프레임워크 활용

React, Vue, Angular 같은 현대적인 프레임워크는 기본적으로 자동 이스케이프를 제공하여 XSS 위험을 줄여줍니다.

// React 예시 - 기본적으로 안전하게 렌더링됨
function Component({ userInput }) {
  return <div>{userInput}</div>; // 자동으로 이스케이프됨
}


CSRF(Cross-Site Request Forgery): 사용자 의도 위조 공격

CSRF는 인증된 사용자가 의도하지 않은 요청을 실행하도록 속이는 공격입니다. 사용자가 로그인된 상태에서 악의적인 사이트를 방문하면, 그 사이트가 사용자 모르게 다른 사이트에 요청을 보낼 수 있습니다.


CSRF 공격 시나리오

  1. 사용자가 은행 사이트(bank.com)에 로그인하여 인증 쿠키를 받습니다.
  2. 로그인 상태에서 악의적인 사이트(evil.com)를 방문합니다.
  3. evil.com에는 다음과 같은 코드가 포함되어 있습니다:
<img src="https://bank.com/transfer?to=attacker&amount=1000" style="display:none">
<!-- 또는 -->
<form id="csrf-form" action="https://bank.com/transfer" method="POST" style="display:none">
  <input type="hidden" name="to" value="attacker">
  <input type="hidden" name="amount" value="1000">
</form>
<script>document.getElementById("csrf-form").submit();</script>
  1. 브라우저는 bank.com에 대한 요청에 자동으로 인증 쿠키를 포함시킵니다.
  2. bank.com은 요청이 인증된 사용자로부터 온 것으로 간주하고 송금을 처리합니다.

CSRF 방어 방법

1. CSRF 토큰 사용

서버가 예측 불가능한 고유 토큰을 생성하여 세션과 연결합니다. 이 토큰은 모든 상태 변경 요청에 포함되어야 합니다.

<!-- 폼에 CSRF 토큰 포함 -->
<form action="/transfer" method="POST">
  <input type="hidden" name="csrf_token" value="random_token_123">
  <!-- 나머지 폼 필드 -->
</form>
// AJAX 요청에 CSRF 토큰 포함
fetch('/api/transfer', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': 'random_token_123'
  },
  body: JSON.stringify(data)
});

서버는 이 토큰을 검증하여 요청의 출처를 확인합니다.

2. SameSite 쿠키 속성

쿠키에 SameSite 속성을 설정하여 교차 사이트 요청에서 쿠키가 전송되는 것을 제한할 수 있습니다.

Set-Cookie: SessionId=abc123; SameSite=Lax; Secure; HttpOnly

SameSite 값에 따른 동작:

  • Strict: 같은 사이트 요청에만 쿠키 전송
  • Lax: 같은 사이트와 최상위 수준 탐색에만 쿠키 전송(브라우저 기본값)
  • None: 교차 사이트 요청에도 쿠키 전송(Secure 플래그 필수)

3. 출처 확인

요청의 Referer 또는 Origin 헤더를 검사하여 예상된 출처에서 왔는지 확인합니다.

세션 저장소 없이 CSRF 공격을 방지하는 방법입니다.

  1. 서버가 무작위 토큰을 생성하여 쿠키로 설정
  2. 같은 값을 요청 파라미터나 헤더로 전송
  3. 서버에 두 값이 일치하는지 확인

5. 커스텀 요청 헤더 사용

AJAX 요청에 커스텀 헤더를 추가하면 단순 폼 제출이나 이미지 로드로는 해당 헤더를 설정할 수 없어 CSRF 공격을 방지할 수 있습니다.



profile
박수 받는 사람이 되고 싶어서 항상 노력합니다.

0개의 댓글