IT & 웹개발

CSRF(Cross-Site Request Forgery) 공격과 방어 방법

kkwako 2025. 3. 27. 13:56

1. CSRF란 무엇인가?

CSRF(Cross-Site Request Forgery, 교차 사이트 요청 위조)는 웹 보안 취약점 중 하나로, 사용자가 의도하지 않은 요청을 특정 웹사이트에 전송하도록 유도하는 공격 기법입니다. 공격자는 피해자가 인증된 상태에서 특정 요청을 수행하도록 만들어 권한이 없는 작업을 실행할 수 있습니다.

예를 들어, 사용자가 은행 웹사이트에 로그인한 상태에서 공격자가 조작된 링크를 클릭하면 피해자의 계정에서 공격자가 지정한 계좌로 돈이 이체될 수도 있습니다. CSRF 공격은 주로 사용자가 신뢰하는 웹사이트의 취약점을 이용하여 발생하며, 피해자는 공격을 인지하지 못한 채 악성 요청을 수행하게 됩니다.

2. CSRF 공격의 동작 방식

CSRF 공격이 발생하는 과정은 다음과 같습니다.

  1. 사용자 로그인: 사용자가 bank.com에 로그인하고, 브라우저는 bank.com의 세션 쿠키를 유지합니다.
  2. 악성 요청 생성: 공격자는 bank.com에서 계좌 이체를 수행하는 URL을 찾아냅니다.
  3. arduino
    복사편집
    https://bank.com/transfer?to=attacker&amount=100000
  4. 피해자를 유도: 공격자는 이메일, 소셜 미디어, 악성 웹사이트 등을 이용하여 피해자가 이 링크를 클릭하도록 유도합니다.
  5. 자동 요청 실행: 피해자가 링크를 클릭하면 브라우저는 로그인된 상태이므로 bank.com의 인증 쿠키를 포함하여 요청을 자동으로 보냅니다.
  6. 공격 성공: 서버는 이 요청이 사용자가 직접 보낸 것인지 확인하지 못하고, 정상적인 요청으로 처리하여 공격자가 원하는 작업을 수행합니다.

이러한 공격은 HTTP 요청이 자동으로 실행되는 방식(Form 자동 제출, 이미지 태그, JavaScript 요청 등)을 악용하여 발생할 수 있습니다.

3. CSRF 방어 방법

CSRF 공격을 방어하는 주요 방법들은 다음과 같습니다.

(1) CSRF 토큰 사용

가장 효과적인 방법 중 하나는 CSRF 토큰을 사용하는 것입니다. CSRF 토큰은 사용자의 세션과 함께 발급되는 고유한 랜덤 문자열로, 요청을 보낼 때마다 함께 전송되어야 합니다. 서버는 이 토큰을 검증하여 요청의 유효성을 확인합니다.

예제 – Express.js에서 CSRF 토큰 적용

javascript
복사편집
const csrf = require('csurf'); const cookieParser = require('cookie-parser'); const express = require('express'); const app = express(); app.use(cookieParser()); app.use(csrf({ cookie: true })); app.get('/form', (req, res) => { res.send(`<form action="/transfer" method="POST"> <input type="hidden" name="_csrf" value="${req.csrfToken()}"> <button type="submit">송금</button> </form>`); }); app.post('/transfer', (req, res) => { res.send('송금 완료'); });

이 방식은 클라이언트가 CSRF 토큰을 포함하지 않으면 요청이 거부되도록 설정하는 방식입니다.

(2) Referer 및 Origin 헤더 검증

서버는 Referer 또는 Origin 헤더를 확인하여 요청이 신뢰할 수 있는 출처에서 왔는지 검사할 수 있습니다.

  • Referer 헤더: 요청을 보낸 페이지의 URL이 포함됨
  • Origin 헤더: 요청이 발생한 출처(도메인 및 프로토콜) 정보 포함

예를 들어, bank.com에서만 요청을 허용하려면 다음과 같이 서버에서 검증할 수 있습니다.

javascript
복사편집
app.post('/transfer', (req, res) => { const allowedOrigin = 'https://bank.com'; const origin = req.get('Origin') || req.get('Referer'); if (!origin || !origin.startsWith(allowedOrigin)) { return res.status(403).send('CSRF 공격이 감지되었습니다.'); } res.send('송금 완료'); });

하지만 일부 브라우저나 네트워크 환경에서는 Referer 헤더가 제거될 수 있으므로 보조적인 방법으로 사용하는 것이 좋습니다.

(3) SameSite 쿠키 설정

CSRF 공격을 방어하는 또 다른 방법은 SameSite 속성을 활용하는 것입니다. SameSite 속성을 Strict 또는 Lax로 설정하면 다른 출처에서 자동으로 쿠키가 전송되지 않아 CSRF 공격을 차단할 수 있습니다.

예제 – Express.js에서 SameSite 설정

javascript
복사편집
const session = require('express-session'); app.use(session({ secret: 'mysecret', resave: false, saveUninitialized: true, cookie: { httpOnly: true, secure: true, sameSite: 'Strict' // 또는 'Lax' } }));
  • Strict: 동일 출처에서만 쿠키 전송(보안 강력, UX 저하 가능)
  • Lax: GET 요청에서는 쿠키 전송 허용, POST에서는 차단(보안과 UX 균형 유지)
  • None: 모든 요청에 대해 쿠키 전송(보안 취약)

이 방식은 브라우저에서 지원해야 적용되므로 최신 브라우저 환경에서만 완벽하게 동작합니다.

(4) 사용자 입력 검증 및 권한 확인

서버는 요청을 처리하기 전에 사용자의 권한을 철저히 검증해야 합니다. 예를 들어, 계좌 이체 요청을 수행할 때 사용자의 비밀번호를 다시 입력하도록 요구하면 CSRF 공격을 방어할 수 있습니다.

html
복사편집
<form action="/transfer" method="POST"> <input type="text" name="to" placeholder="받는 사람"> <input type="number" name="amount" placeholder="금액"> <input type="password" name="password" placeholder="비밀번호 확인"> <button type="submit">송금</button> </form>

비밀번호를 입력해야만 요청이 완료되므로, 공격자가 자동화된 CSRF 공격을 수행하기 어렵습니다.

 

CSRF(Cross-Site Request Forgery) 공격과 방어 방법

 

4. CSRF 공격과 XSS의 차이점

CSRF와 XSS(Cross-Site Scripting)는 종종 혼동되지만, 서로 다른 공격 방식입니다.

구분CSRFXSS
공격 방식 사용자의 인증된 세션을 이용하여 악성 요청을 전송 웹사이트에 악성 스크립트를 삽입하여 실행
공격 대상 서버 측 클라이언트 측(사용자의 브라우저)
주요 원인 서버에서 요청의 출처를 검증하지 않음 사용자 입력값을 필터링하지 않음
방어 방법 CSRF 토큰, SameSite 쿠키, Referer 검사 입력값 필터링, CSP(Content Security Policy) 적용

CSRF는 서버가 신뢰하는 사용자의 세션을 가로채는 방식이고, XSS는 사용자의 브라우저에서 악성 코드가 실행되도록 하는 방식이라는 점에서 차이가 있습니다.

5. 결론

CSRF 공격은 사용자의 권한을 도용하여 의도하지 않은 요청을 수행하도록 만드는 보안 취약점입니다. 이를 방어하기 위해서는 CSRF 토큰을 사용하고, SameSite 쿠키 설정, Referer 및 Origin 검증, 사용자 입력 확인 등의 방법을 적용해야 합니다. 특히, 여러 보안 조치를 함께 사용하는 것이 가장 효과적이며, 최신 브라우저와 서버 보안 정책을 활용하여 더욱 강력한 보안 환경을 구축하는 것이 중요합니다.