← 홈페이지 5강 목록으로
🔐
EPISODE 02
SameSite 쿠키 · CSRF 토큰 · 세션 vs JWT · httpOnly

CSRF 방어와 인증

CSRF 공격 시나리오, SameSite 쿠키와 CSRF 토큰 두 가지 방어법, 세션과 JWT의 동작 차이, 그리고 httpOnly·secure·sameSite·maxAge 보안 쿠키 속성을 익힙니다.

CSRFJWTsessioncookie
소요 시간
45~60분
난이도
📊 중급
선수 조건
🎯 sec-01
결과물
안전한 인증 쿠키와 CSRF 토큰을 가진 폼

이 강의에서 배우는 것

  • 1CSRF 공격 시나리오를 설명한다
  • 2SameSite=Strict/Lax/None 의 동작을 안다
  • 3CSRF 토큰 발급·검증 미들웨어를 작성한다
  • 4세션과 JWT의 장단점을 비교한다
  • 5httpOnly/secure/sameSite/maxAge 4종 세트를 적용한다

1. CSRF란

인증된 사용자를 속여 의도하지 않은 요청을 서버에 보내게 하는 공격.

text
1. 피해자가 bank.com 로그인 (세션 쿠키)
2. 공격자가 evil.com에 악성 폼 심어둠:
   <form action="https://bank.com/transfer" method="POST">
     <input name="to"     value="공격자계좌">
     <input name="amount" value="1000000">
   </form>
   <script>document.forms[0].submit();</script>
3. 피해자가 evil.com 방문
4. 브라우저가 bank.com에 자동으로 쿠키 포함하여 POST
5. bank.com은 유효한 세션으로 판단 → 송금 실행!

2. SameSite 쿠키 (현대적 방어)

javascript
// Strict: 같은 사이트에서의 요청에만
res.cookie('sessionId', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'Strict',
});

// Lax: GET은 허용, POST는 차단 (권장)
res.cookie('sessionId', token, { sameSite: 'Lax' });

// None: 크로스사이트 허용 (Secure 필수)
res.cookie('sessionId', token, { sameSite: 'None', secure: true });
동작
Strict외부 사이트 링크로 온 GET도 쿠키 없음 (가장 엄격)
Lax외부 GET은 허용, POST 등은 차단 (권장)
None모두 허용 (Secure 필수)

3. CSRF 토큰

javascript
// 1. 토큰 생성 (서버)
const crypto = require('crypto');
const csrfToken = crypto.randomBytes(32).toString('hex');
req.session.csrfToken = csrfToken;
res.render('form', { csrfToken });
html
<form method="POST" action="/transfer">
  <input type="hidden" name="_csrf" value="<%= csrfToken %>">
  <input name="amount" type="number">
  <button type="submit">송금</button>
</form>
javascript
function csrfProtect(req, res, next) {
  if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(req.method)) {
    const submitted = req.body._csrf || req.headers['x-csrf-token'];
    const stored = req.session.csrfToken;
    if (!submitted || submitted !== stored) {
      return res.status(403).json({ error: 'CSRF 토큰 검증 실패' });
    }
  }
  next();
}

4. 세션 vs JWT

세션

text
[클라이언트] ── POST /login ──> [서버]
                              세션 생성 { userId: 1 }
              <── Set-Cookie: sid=abc ── 저장소(Redis/DB)에 저장

[클라이언트] ── GET /profile ──> [서버] (Cookie: sid=abc)
                              저장소에서 세션 조회 → userId 확인

장점: 즉시 무효화 가능 / 단점: 저장소 필요, 수평 확장 시 세션 공유

JWT

text
[클라이언트] ── POST /login ──> [서버]
                              JWT 생성 { userId: 1, exp }
              <── JWT 토큰 ────── (서버에 저장 안 함)

[클라이언트] ── GET /profile ──> [서버] (Authorization: Bearer eyJ...)
                              JWT 서명 검증 → userId 확인

장점: 저장소 불필요, 수평 확장 / 단점: 만료 전 무효화 어려움

5. httpOnly / Secure 쿠키

javascript
res.cookie('token', value, {
  httpOnly: true,    // JS에서 document.cookie 접근 불가 → XSS로 쿠키 탈취 방지
  secure: true,      // HTTPS에서만 전송 → 도청 방지
  sameSite: 'Lax',  // CSRF 방어
  maxAge: 7 * 24 * 60 * 60 * 1000,  // 7일
  path: '/',
});
속성목적
httpOnlyXSS를 통한 쿠키 탈취 방지
secure네트워크 도청 방지
sameSiteCSRF 방지
maxAge세션 하이재킹 최소화 (짧을수록 안전)
예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗