📎

CSRF, 클릭재킹, 오픈 리다이렉트

1. CSRF 보안 방지

CSRF(Cross-Site Request Forgery) 공격은 사용자가 의도하지 않은 요청을 악성 사이트를 통해 전송하게 만드는 공격이다.

공격 시나리오

  1. 사용자가 은행 사이트에 로그인한 상태에서 악성 사이트를 방문한다
  1. 악성 사이트에 숨겨진 폼이 자동으로 은행 사이트로 송금 요청을 보낸다
  1. 브라우저가 자동으로 쿠키를 포함해서 요청을 전송한다
  1. 서버는 정상적인 요청으로 인식하고 송금을 처리한다

방어 기법

세션별 원타임 토큰 (CSRF Token)

서버에서 생성한 고유한 토큰을 폼에 포함시키고, 요청 시 토큰을 검증하는 방식이다.
<form action="/transfer" method="post"> <input type="hidden" name="csrf_token" value="abc123xyz"> <input type="text" name="amount" value="1000"> <button type="submit">송금</button> </form>

Double Submit 쿠키

CSRF 토큰을 쿠키와 폼 데이터 양쪽에 포함시켜 검증하는 방식이다. HttpOnly가 아닌 쿠키로 설정하여 자바스크립트에서 읽을 수 있게 한다.
// 쿠키에서 토큰 읽기 const token = getCookie('csrf_token'); // 요청 헤더에 포함 fetch('/api/data', { headers: { 'X-CSRF-Token': token } });

SameSite 쿠키

쿠키의 SameSite 속성을 사용하여 교차 사이트 요청에서 쿠키 전송을 제한한다.
Set-Cookie: sessionid=abc123; SameSite=Strict Set-Cookie: sessionid=abc123; SameSite=Lax
  • Strict 모든 교차 사이트 요청에서 쿠키를 차단한다
  • Lax GET 요청과 최상위 네비게이션에서만 쿠키를 허용한다

Origin/Referer 헤더 검증

요청의 출처를 확인하여 허용된 도메인에서만 요청을 받는다.
app.use((req, res, next) => { const origin = req.get('Origin') || req.get('Referer'); if (!origin || !origin.startsWith('https://trusted-domain.com')) { return res.status(403).send('Forbidden'); } next(); });

2. 클릭재킹 (Clickjacking)

클릭재킹은 투명한 iframe을 사용하여 사용자가 의도하지 않은 버튼이나 링크를 클릭하게 만드는 공격이다.

공격 시나리오

  1. 공격자가 피해자 사이트를 투명한 iframe으로 삽입한다
  1. iframe 위에 가짜 버튼이나 링크를 배치한다
  1. 사용자가 가짜 요소를 클릭하면 실제로는 iframe 내부의 버튼이 클릭된다
  1. 사용자 모르게 계정 삭제, 비밀번호 변경 등이 실행된다

방어 기법

X-Frame-Options 헤더

웹 페이지가 iframe 내부에 삽입되는 것을 제한한다.
X-Frame-Options: DENY X-Frame-Options: SAMEORIGIN X-Frame-Options: ALLOW-FROM https://trusted-site.com

CSP frame-ancestors

Content Security Policy를 사용하여 더 세밀한 제어가 가능하다.
Content-Security-Policy: frame-ancestors 'none' Content-Security-Policy: frame-ancestors 'self' Content-Security-Policy: frame-ancestors https://trusted-site.com

자바스크립트 프레임 버스터

클라이언트 측에서 프레임 삽입을 감지하고 차단한다.
if (top !== self) { top.location = self.location; }

3. 오픈 리다이렉트

웹 애플리케이션의 리다이렉트 기능을 악용하여 사용자를 악성 사이트로 유도하는 공격이다.

공격 시나리오

  1. 공격자가 신뢰할 수 있는 사이트의 리다이렉트 URL을 조작한다
    1. https://trusted-site.com/redirect?url=https://malicious-site.com
  1. 사용자가 링크를 클릭하면 악성 사이트로 이동한다
  1. 사용자는 신뢰할 수 있는 도메인에서 시작된 링크라고 생각하여 경계심을 늦춘다

방어 기법

URL 화이트리스트 검증

허용된 도메인 목록을 만들어 리다이렉트 URL을 검증한다.
const allowedDomains = ['trusted-site.com', 'partner-site.com']; function isValidRedirectUrl(url) { try { const urlObj = new URL(url); return allowedDomains.includes(urlObj.hostname); } catch { return false; } }

상대 경로만 허용

절대 URL 대신 상대 경로만 허용하여 외부 사이트로의 리다이렉트를 차단한다.
function validateRedirect(path) { // 프로토콜이 포함된 URL 차단 if (path.includes('://')) { return false; } // 이중 슬래시로 시작하는 URL 차단 (//evil.com) if (path.startsWith('//')) { return false; } return true; }

경고 페이지 사용

외부 사이트로 이동하기 전에 경고 페이지를 표시한다.
app.get('/redirect', (req, res) => { const targetUrl = req.query.url; if (!isInternalUrl(targetUrl)) { res.render('warning', { targetUrl }); } else { res.redirect(targetUrl); } });