CSRF 보호
규칙
- 모든 쿠키에
SameSite=Lax 설정 — 기본적으로 대부분의 CSRF 공격 차단
- 매우 민감한 작업(계정 삭제, 비밀번호 변경)에는
SameSite=Strict 사용
- 사용자 정의 API에 이중 제출 쿠키 패턴 구현: 쿠키 속 CSRF 토큰 + 요청 헤더
- 상태 변경을 수행하는 모든 HTML 폼에 CSRF 토큰 포함
- 상태 변경 요청 시
Origin 및 Referer 헤더 검증 — 방어 심층화
- Next.js Server Actions와 SvelteKit form actions는 CSRF를 자동 처리함 — 사용 권장
- SPA의 경우: CSRF 토큰을 사용자 정의 헤더(
X-CSRF-Token)로 전송 — 브라우저는 기본적으로 교차 출처 사용자 정의 헤더 차단
import crypto from "crypto";
function generateCsrfToken(res) {
const token = crypto.randomBytes(32).toString("hex");
res.cookie("csrf_token", token, { httpOnly: false, sameSite: "strict", secure: true });
return token;
}
function verifyCsrf(req) {
const cookieToken = req.cookies.csrf_token;
const headerToken = req.headers["x-csrf-token"];
if (!cookieToken || cookieToken !== headerToken) {
throw new Error("CSRF 검증 실패");
}
}
피해야 할 것
- CSRF 보호 없이 쿠키 기반 인증만 사용하는 것 — 어떤 사이트든 엔드포인트에 폼을 제출할 수 있음
- CSRF 토큰을 절대 갱신하지 않는 것 — 하나의 토큰이 유출되면 모든 미래 요청이 위험해짐
- 상태를 변경하는 GET 요청 — CSRF 보호는 POST/PUT/DELETE에서만 작동하며 GET 요청은 SameSite=Lax를 우회
- "그냥 API니까"라고 CSRF 보호를 비활성화하는 것 — 쿠키가 관여된다면 CSRF가 적용됨