쉬운 설명
당신이 은행 사이트에 로그인된 상태로 있을 때, 다른 탭에서 악성 사이트가 몰래 '돈을 보내라'는 요청을 그 은행 API로 보냅니다. 브라우저는 그 요청에 자동으로 당신의 로그인 쿠키를 첨부해 보냅니다. 은행 서버는 '제대로 로그인한 사용자의 요청'으로 받아들이고 실행해 버립니다. 이게 CSRF입니다.
왜 이런 공격이 가능한가 하면, 브라우저가 같은 도메인에 요청을 보낼 때 그 도메인의 쿠키를 자동으로 첨부하기 때문입니다. 이 자동 첨부는 보통 편리하지만, 공격자가 사용자를 속여 악성 페이지에 들어오게 하면 같은 메커니즘이 공격 도구가 됩니다. 사용자는 클릭 한 번도 안 했는데 자기 계정으로 위험한 동작이 실행됩니다.
방어의 핵심은 '이 요청이 정말 우리 사이트에서 시작된 것인지 확인'하는 데 있습니다. 대표 기법: ① CSRF 토큰(폼 제출 시 서버가 발급한 임의 토큰을 함께 보내야 인정), ② SameSite 쿠키 속성(다른 사이트가 쿠키를 동반한 요청을 보내지 못하게 막기 — 현재 대부분 브라우저의 기본값), ③ 중요한 동작은 비밀번호·OTP를 다시 한 번 받기(재인증), ④ Origin·Referer 헤더 검증.
많은 사람이 'HTTPS를 쓰면 안전하지 않나'라고 묻지만, HTTPS는 통신 도청을 막는 것이지 다른 사이트가 보내는 요청을 막아 주지는 않습니다. CSRF는 인증된 사용자의 '권한'을 빌려 쓰는 공격이라, 별도의 방어가 꼭 필요합니다.
최근에는 SameSite 쿠키의 보급으로 CSRF의 가장 흔한 형태가 자동 차단됩니다. 그래도 완벽한 방어는 아니고, 특히 사용자가 직접 다른 사이트를 통해 우리 사이트로 이동하는 흐름에선 여전히 주의가 필요합니다. 보안은 한 층이 아니라 여러 층으로 쌓는 게 원칙입니다.

비유로 보면
CSRF는 누군가가 당신의 인감을 몰래 꺼내 다른 사람의 서류에 찍는 일과 비슷합니다. 인감(쿠키)은 진짜 당신 것이고 도장도 정확하지만, 당신은 그 서류에 도장을 찍은 적이 없습니다. 인감을 안전한 곳에 보관하거나(SameSite), 도장을 찍을 때마다 본인 확인을 한 번 더 하면(재인증·CSRF 토큰) 이런 일을 막을 수 있습니다.
어디에서 만나나
사용자 인증이 있는 모든 웹 서비스가 CSRF를 신경 써야 합니다. 특히 폼 제출·계정 변경·금융 거래·이메일 발송 같은 '되돌릴 수 없는' 동작에 가장 중요합니다. 현대 프레임워크(Django·Rails·Spring·NestJS 등)는 기본 CSRF 보호를 포함하고 있어, 일부러 끄지 않는 한 자동 동작합니다.
작은 예시
사용자가 로그인된 사내 시스템 탭이 떠 있을 때, 누가 보낸 메일의 링크를 무심코 클릭합니다. 그 링크가 사용자 권한으로 '내 계정을 다른 사람에게 양도' 요청을 사내 시스템에 보내고, CSRF 방어가 약하면 그 요청이 정상으로 처리됩니다. CSRF 토큰·SameSite 설정이 이걸 막아 줍니다.
자주 하는 오해
한 줄 정리
CSRF의 방어는 한 층으로는 부족합니다. SameSite 쿠키 + CSRF 토큰 + 중요한 동작의 재인증 — 여러 층을 함께 두는 게 표준입니다.
