Spring Boot 프로젝트를 하던중에 Security 관련 공부를 하면서 csrf 설정을 해야한다는 것을 보고, csrf가 궁금해져서 알아보게 되었습니다. 그리고 비슷한 개념인 XSS에 대해서도 살짝 맛보도록 하겠습니다.


1. CSRF(Cross-Site Request Forgery: 크로스 사이트 요청 위조)

CSRF는 Cross-Site Request Forgery의 약자로, 번역하면 사이트 간 요청 위조를 의미합니다.

CSRF는 웹사이트의 취약점을 이용하여 사용자의 의지와 무관하게 공격자가 의도한 행위(데이터 삭제, 수정, 등록 등)을 특정 웹사이트에 요청하게 하는 공격입니다.

생성된 요청이 인증된 사용자의 동의를 받았는지 확인할 수 없는 웹 애플리케이션의 CSRF 취약점을 이용하는 것입니다.

  • 사용자가 인증한 세션에서 웹 애플리케이션이 정상적인 요청과 비정상적인 요청을 구분하지 못하는 점을 악용하는 공격 방식

공격자의 요청이 사용자의 요청인 것처럼 속여서 공격하는 방식이기에 '크로스 사이트 요청 위조'라는 명칭이 붙었습니다.

 

CSRF 공격 시나리오

CSRF 공격의 시나리오는 아래와 같습니다.

1️⃣ 사용자가 웹사이트에 접속해서 로그인하여 권한을 인증하고 세션을 생성합니다.

2️⃣ 공격자는 악성 웹사이트로 사용자를 유인합니다. 사용자가 이 링크를 클릭하면 공격자의 악성 웹사이트로 이동하게 됩니다.

3️⃣ 사용자가 악성 웹사이트에 방문하면, 해당 페이지에 포함된 스크립트나 이미지 태그가 사용자가 로그인한 웹사이트에 요청을 자동으로 전송합니다.

  • 예를 들어, 은행 사이트라고 할 때, 공격자가 이미지 태그로 돈을 송금하는 요청을 보낸다고 해봅시다.
< img  src = "http://bank.com/transfer?amount=9999&toAccount=000011112222" />

해당 이미지 태그를 악성 웹사이트에 심어두면 사용자가 접속시 브라우저가 이 이미지를 로드하기 때문에, 해당 은행 웹사이트로 돈 송금 요청이 보내지게 됩니다.

4️⃣ 사용자는 웹사이트에 이미 인증을 한 상태이므로, 해당 요청은 서버에서 사용자의 정상적인 요청으로 판단하고 인증된 요청으로 처리합니다.

5️⃣ 서버는 이 악의적인 요청을 처리하여 사용자가 의도하지 않은, 공격자가 의도한 요청을 수행하게 됩니다.

  • 예시로 보면, 사용자는 의도하지 않은 요청인, '000011112222' 계좌로 9,999원을 송금하게 됩니다.

 

CSRF 공격 사례

CSRF는 데이터 값을 변경하는 요청을 대상으로 합니다.

그러한 요청으로는 제품을 구입하거나 계정 설정, 기록을 삭제하거나 비밀번호를 변경하는 등의 요청이 있습니다. 

 

실제로 2008년에 옥션이 중국인 해커에게 CSRF 공격 방식으로 회원정보가 유출된 사례가 있습니다.

해커는 권한을 가지고 회사 내 작업을 하던 옥션 관리자에게 메일을 보냈고, 관리자는 이 메일을 조회했습니다. 

해커가 보낸 메일에는 태그가 들어간 코드가 포함되어 있었습니다. 

< img  src = "http://auction.com/changeUserAccount?id=admin&password=admin"  너비 = "0"  높이 = "0" >

(이미지 크기가 0이므로 관리자는 이 존재를 알지 못합니다.)

관리자가 메일을 조회하는 순간 이미지 파일을 받아오기 위해 URL이 열리게 되고, 해커가 원하는대로 관리자의 계정이 해커가 설정한 "admin"으로 변경됩니다. 이후 해커는 이 계정으로 서버 관리자 페이지에 접속해서 *백도어 프로그램을 올렸습니다.

*백도어 프로그램: 정상적인 절차를 거치지 않고도 접근을 가능하게 해주는 프로그램

백도어 프로그램을 이용해서 옥션 내부 웹 서버에 접속해 데이터베이스를 해킹했고, 회원 정보를 빼내간 것입니다.

이때 유출된 회원 정보가 1860만명이라고 합니다.

 

이러한 CSRF 공격에 당하게 되면 막대한 피해가 발생할 수 있습니다. 그렇기 때문에 웹 애플리케이션은 CSRF 공격을 방지하기 위해 CSRF 토큰을 통해 해당 요청이 인증된 사용자가 전송한 것이 맞는지를 확인하거나 재인증을 요구하는 등의 방식으로 CSRF 공격을 방지해야합니다.


2. CSRF 공격 방지 - Synchronizer Token Pattern

Spring에서는 CSRF 공격을 방지하기 위한 메커니즘 중 Synchronizer Token Pattern을 가장 우세하고 포괄적인 방법으로 소개하고 있습니다. Synchronizer Token Pattern은 CSRF 공격으로부터 애플리케이션을 보호하기 위해 서버와 클라이언트 간에 고유한 보안 토큰인 CSRF 토큰을 사용하는 방법입니다.

 

🔐 CSRF 토큰

사용자가 인증을 하더라도 CSRF 공격이 가능하기 때문에 보통 JWT만을 이용하여 사용자를 인증한다하였을때에도 공격을 받을 수 있습니다. 그래서 CSRF 토큰을 사용해서 CSRF 공격을 방지합니다.

 

CSRF 토큰이란, 서버에 들어온 요청이 실제 서버에서 허용한 요청이 맞는지를 확인하기 위한 토큰입니다.

CSRF 토큰은 서버에서 생성해서 클라이언트와 공유되는 인증 값입니다. 클라이언트는 서버에 요청시에 서버에서 인증해준 CSRF 토큰을 포함해서 요청해야합니다. CSRF 토큰이 없으면 서버는 요청을 거부하게 됩니다. 

 

CSRF 토큰을 HTML 폼에 담아서 보내는 방식이 일반적으로 사용되는 방식입니다.

 

1️⃣ 클라이언트는 애플리케이션 서버에 HTTP GET 요청을 통해 액세스합니다.

2️⃣ 서버는 CSRF 토큰을 생성하고 HTTP 세션에 저장합니다. 생성된 CSRF 토큰은 HTML 형식의 숨겨진 태그(hidden)를 사용하여 클라이언트와 연결됩니다.

3️⃣ 클라이언트는 HTML 폼의 버튼을 통해 애플리케이션 서버에 요청을 보냅니다. CSRF 토큰은 HTML 폼의 Hidden 필드에 포함되어 있으므로 CSRF 토큰 값이 요청 매개변수로 함께 전송됩니다. 

4️⃣ 서버는 요청 파라미터에 지정된 CSRF 토큰 값과 HTTP POST 메서드를 사용하여 액세스 할 때 HTTP 세션에 유지된 CSRF 토큰 값이 동일한지 확인합니다. 토큰 값이 일치하지 않으면 잘못된 요청으로, 오류가 발생합니다.

 

다시 정리하자면, 아래와 같습니다.

  • 로그인한 사용자가 페이지에 접속시 서버에서는 CSRF 토큰을 생성해서 사용자 세션이 저장합니다.
  • 이후 사용자가 서버에 애플맄이션의 상태를 변경하는 작업을 요청할 때 페이지에 Hidden 으로 숨어있는 CSRF 토큰 값이 서버로 전송됩니다. 
  • 서버에서는 이 CSRF 값이 세션에 저장된 값과 일치하는지 확인하여 해당 요청이 정상적인 요청임을 확인할 수 있습니다. 

CSRF 토큰 유의사항

[CSRF 토큰의 위치]

  • HTML 폼 제출시 hidden 필드로 포함시켜서 전송해야합니다.
  • AJAX 요청을 보낼시에는 CSRF 토큰을 Custom Header 값으로 포함시키거나 JSON payload(데이터)로 전송할 수 있습니다.
  • CSRF 토큰을 쿠키에 포함시켜서 전송하면 안됩니다.
    • CSRF 토큰은 실제 사용자가 요청한 정상적인 작업인지를 검증해야하는데, 쿠키는 브라우저에 의해 자동으로 HTTP 요청에 포함되므로, 정상적인 요청인지 검증할 수 없게 됩니다.
    • '자동 전송'이라는 기능때문에 공격자가 만든 요청이여도 쿠키가 자동으로 포함되어 서버에 전송됩니다. 이로인해 서버는 이 요청이 정상적인 요청이라고 착각하여 요청을 수행할 수도 있습니다.
    • 따라서 CSRF 토큰은 사용자가 직접 제어할 수 있는 방식으로 전송되어야 합니다.
  • CSRF 토큰은 서버 로그나 URL에 노출되어서는 안됩니다.

[HTTP 요청에 따른 요구사항]

  • 애플리케이션의 상태를 변경하는 요청에 대해서만 CSRF를 요청해도 괜찮습니다. (POST, PUT, PATCH, DELETE)
    • 단, 이를 위해서는 상태가 변경되지 않는(=멱등 요청)요청(GET, HEAD, OPTIONS, 및 TRACE)이 오직 읽기 전용(read-only) 작업만 수행하도록 보장해야합니다.
  • HTTP GET(상태가 변경되지 않는 안전한 요청)에는 CSRF 토큰을 포함시키지 않아야합니다.
    • GET 요청은 캐시되거나 URL로 노출될 수 있기 때문에 CSRF 토큰이 유출될 수도 있습니다.
    • 토큰이 유출되면 공격자가 이 토큰을 재사용하여 CSRF 공격을 시도할 수도 있습니다.

3. CSRF vs XSS

CSRF와 XSS는 모두 웹 애플리케이션에서의 공격 유형이지만, 차이점이 있습니다.

먼저 XSS가 무엇인지 간단하게 짚고 넘어가겠습니다.

 

XSS(Cross-Site Scripting)

XSS는 Cross-Site Scripting의 약자로, 번역하면 사이트 간 스크립팅 입니다.

XSS 공격은 공격자가 웹 페이지에 악의적인 스크립트를 삽입하여, 사용자의 브라우저에서 실행시키는 공격입니다. 

이로인해 사용자는 의도하지 않은 동작을 수행하거나, 공격자에게 쿠키, 세션 등의 정보를 탈취 당하게 됩니다.

XSS는 SQL Injection과 함께 웹 상에서 가장 기초적인 취약점 공격 방법 중 하나입니다.

XSS 공격 대응 방안

  • 중요 정보는 쿠키 대신 서버에 저장합니다.
  • 정보를 암호화 합니다.
  • httpOnly 옵션을 설정합니다.
    • document.cookie를 이용해 쿠키에 직접 접근하는 것을 방지
  • URL Encoding이나 문자열을 치환합니다.

CSRF 와 XSS 의 차이

[공격 대상]

  • CSRF 공격 대상은 "Server"이고 XSS 공격 대상은 "Client"입니다.
  • CSRF는 특정 웹 사이트가 사용자의 웹 브라우저를 신뢰하는 상태를 노린 것이고, XSS는 사용자가 특정 웹 사이트를 신뢰하는 점을 노린 것입니다.

[공격 방식]

  • CSRF공격자가 사용자의 브라우저를 속여 의도하지 않은 요청을 특정 웹사이트로 보내도록 유도합니다. 사용자의 권한을 이용해 서버에 대한 악성 공격을을 하게 하는 것입니다. 
  • XSS공격자가 악성 스크립트를 웹페이지에 삽입하여 해당 페이지를 방문하는 사용자의 브라우저에서 스크립트가 실행되게 합니다. 스크립트는 사용자의 브라우저에서 실행되므로 사용자의 권한으로 민감한 데이터를 탈취하거나 임의의 명령을 실행할 수 있습니다.

CSRF 공격

1️⃣ 공격자자 웹 사이트에 자금 이체 요청을 위조합니다.

2️⃣ 공격자는 그 요청을 하이퍼링크(스크립트)에 삽입하여 웹 사이트에 로그인 할 사용자에게 전송합니다.

3️⃣ 사용자가 그 링크를 클릭하면, 사용자도 모르게 웹 사이트에 요청을 전송하게 됩니다.

4️⃣ 웹 사이트의 서버는 로그인 된 사용자의 요청이기 때문에 정상으로 판단하고, 사용자의 계정에서 공격자의 계정으로 자금을 이체합니다.

XSS 공격

1️⃣ 공격자가 스크립트 주입이 가능한 취약점이 있는 웹 사이트를 찾습니다.

2️⃣ 취약점을 찾아 세션 쿠키를 탈취하는 악성 스크립트를 사이트에 삽입합니다.

3️⃣ 사용자가 웹 사이트를 방문할 때마다 스크립트가 작동됩니다.

4️⃣ 작동된 스크립트를 통해 사용자의 세션 쿠키를 탈취합니다.


Reference

 

 

Cross Site Request Forgery (CSRF) :: Spring Security

When should you use CSRF protection? Our recommendation is to use CSRF protection for any request that could be processed by a browser by normal users. If you are creating a service that is used only by non-browser clients, you likely want to disable CSRF

docs.spring.io

 

웹 보안의 기본: CSRF와 XSS 공격 이해하기

CSRF와 XSS 공격의 개념을 이해하고, 이를 방지하기 위한 기본적인 방법들에 대해 알아보는 글입니다.

f-lab.kr

 
[보안] CSRF

여기서는 CSRF 공격이 무엇인지 알아보고, 이것을 예방하기 위해서 사용되는 CSRF Token에 대해서 정리를 할 것입니다.

벨로그.아이오
 

XSS와 CSRF 차이점 및 대응 방안

웹사이트에서 의도치 않은 스크립트를 넣어서 실행시키는 기법을 말합니다.웹 애플리케이션이 사용자로부터 입력 받은 값을 제대로 검사하지 않고 사용할 경우 발생하며, 결과로 사용자는 의

velog.io

+ Recent posts