1. CSRF 보안 방지
CSRF(Cross-Site Request Forgery) 공격은 사용자가 의도하지 않은 요청을 악성 사이트를 통해 전송하게 만드는 공격이다.
공격 시나리오
- 사용자가 은행 사이트에 로그인한 상태에서 악성 사이트를 방문한다
- 악성 사이트에 숨겨진 폼이 자동으로 은행 사이트로 송금 요청을 보낸다
- 브라우저가 자동으로 쿠키를 포함해서 요청을 전송한다
- 서버는 정상적인 요청으로 인식하고 송금을 처리한다
방어 기법
세션별 원타임 토큰 (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을 사용하여 사용자가 의도하지 않은 버튼이나 링크를 클릭하게 만드는 공격이다.
공격 시나리오
- 공격자가 피해자 사이트를 투명한 iframe으로 삽입한다
- iframe 위에 가짜 버튼이나 링크를 배치한다
- 사용자가 가짜 요소를 클릭하면 실제로는 iframe 내부의 버튼이 클릭된다
- 사용자 모르게 계정 삭제, 비밀번호 변경 등이 실행된다
방어 기법
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. 오픈 리다이렉트
웹 애플리케이션의 리다이렉트 기능을 악용하여 사용자를 악성 사이트로 유도하는 공격이다.
공격 시나리오
- 공격자가 신뢰할 수 있는 사이트의 리다이렉트 URL을 조작한다
https://trusted-site.com/redirect?url=https://malicious-site.com
- 사용자가 링크를 클릭하면 악성 사이트로 이동한다
- 사용자는 신뢰할 수 있는 도메인에서 시작된 링크라고 생각하여 경계심을 늦춘다
방어 기법
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); } });