CSRF는 로그인된 채로 다른 사이트를 열기만 했는데 자신도 모르게 송금이나 설정 변경이 일어나는 공격입니다. 작동 원리와 확실히 막는 법을 살펴봅니다(공격 절차는 다루지 않습니다).
왜 통하는가(메커니즘)
브라우저는 어떤 사이트에 대한 요청에 그 사이트의 쿠키를 자동으로 붙입니다. 그래서 사용자가 은행에 로그인되어 있다면, 악성 페이지에서 트리거된 은행으로의 요청에도 "사용자의 쿠키"가 그대로 실립니다. 서버 입장에서는 정당한 행동과 구분할 수 없습니다.
XSS가 "피해자의 브라우저에서 코드를 실행"하는 것이라면, CSRF는 "피해자의 로그인 상태를 빌리는" 것입니다(→ XSS란).
방어
CSRF 토큰 요구
상태를 바꾸는 행동(폼 제출, API)에는 서버가 발급한, 추측 불가능한 일회성 토큰을 요구하고 토큰이 없는 요청은 거부하세요. 대부분의 프레임워크가 이를 기본 제공합니다.
SameSite 쿠키 설정
세션 쿠키에 SameSite=Lax(또는 Strict)를 지정해, 크로스 사이트에서 시작된 요청에 쿠키가 붙지 않게 하세요. Lax는 현대의 기본값이며 그 자체로 큰 도움이 됩니다.
상태 변경에 GET을 쓰지 않기
GET(조회)은 부작용이 없어야 합니다. 변경은 POST/PUT/DELETE로 만들고 토큰 검증을 거치게 하세요.
Origin / Referer 검사
민감한 행동에 대해서는 요청의 Origin이 내 사이트인지 확인하고, 외부 오리진은 거부하세요.
본 사이트의 견해: 대체로 '해결됨'이지만 방심은 금물
프레임워크의 CSRF 토큰과 브라우저의 SameSite=Lax 기본값이 고전적 CSRF를 크게 줄였습니다. 그래도 커스텀 API, 레거시 구성, 토큰을 건너뛴 관리자 패널에서는 여전히 발생합니다. "프레임워크에 맡긴다" 하더라도, 상태 변경 경로가 실제로 토큰 검증을 거치는지는 한 번 직접 확인하세요.
흔한 빈틈
메커니즘을 알더라도 구현은 두 곳에서 미끄러지기 쉽습니다.
- 부작용 있는 GET: 조회용 GET이 상태를 바꾸면, 이미지 태그나 평범한 링크 하나로도 CSRF가 됩니다. 상태 변경은 POST/PUT/DELETE로 만들고 토큰 검증을 거치게 하세요.
- 일부 경로의 토큰 검증 누락: 나중에 추가된 API와 관리자 기능은 종종 토큰을 건너뜁니다. "프레임워크에 있겠지"라고 가정하지 말고, 상태를 바꾸는 모든 경로를 한 번 정리해 점검하세요.
CSRF vs XSS vs SSRF
이름이 비슷한 세 가지는 누가 요청을 보내는지, 무엇에 기대는지로 구별할 수 있습니다.
| 공격 | 요청을 보내는 주체 | 악용되는 것 | 주된 방어 |
|---|---|---|---|
| CSRF | 사용자가 모르는 사이 사용자의 브라우저 | 브라우저가 쿠키를 자동으로 붙이는 성질 | CSRF 토큰, SameSite 쿠키 |
| XSS | 사용자의 브라우저에서 실행되는 공격자의 스크립트 | 페이지를 출력할 때 이스케이프 누락 | 출력 시 이스케이프, CSP |
| SSRF | 내 서버 | 받은 URL을 가져오는 기능 | 목적지 허용 목록, 내부 주소 차단 |
CSRF와 XSS는 둘 다 사용자의 브라우저를 통해 일어납니다. 다만 페이지에 XSS가 있으면 그 안의 스크립트가 CSRF 토큰을 읽을 수 있어 CSRF 대책이 우회됩니다. CSRF 대책은 XSS가 이미 해결되었다는 전제 위에 서 있습니다.
실제 사례: 위조된 글이 본인의 글로 취급되다
2012년 요코하마시 웹사이트의 의견 접수 폼으로 초등학교를 습격하겠다는 협박 글이 올라왔습니다. 글을 보낸 것처럼 보인 PC의 주인이 체포되었지만, 나중에 오인 체포였던 것으로 밝혀졌습니다. 일본 언론 보도에 따르면 그해 12월 도쿄 경시청은 이 글이 CSRF로 전송되었다고 설명했습니다. 게시판에 올라온 링크를 연 방문자의 브라우저가 글을 제출하게 만든 것입니다.
교훈은 두 가지입니다.
- CSRF 요청은 본인의 요청과 똑같아 보인다: 서버에 보이는 것은 사용자의 IP 주소와 쿠키뿐입니다. 로그만으로는 사용자가 보냈는지, 보내게 만들어졌는지 구별할 수 없습니다.
- 로그인이 없는 폼도 대상이다: 그 의견 접수 폼은 로그인이 필요 없었습니다. 로그인 필요 여부와 상관없이, 남이 일으키면 곤란한 제출(게시, 신청, 신고)에는 토큰을 붙이세요.
CSRF는 지금도 흔한 약점입니다. 2025년 12월 공개된 MITRE의 "CWE Top 25"(가장 위험한 소프트웨어 약점 25개)에서 CSRF(CWE-352)는 3위였습니다.
내 사이트에서 확인하는 방법
세션 쿠키에 SameSite를 명시한다
브라우저 개발자 도구(Application → Cookies)나 사이트 점검으로 세션 쿠키의 SameSite 속성을 확인합니다. Chrome과 Edge는 속성이 없는 쿠키를 Lax로 취급하지만, Safari와 Firefox는 기본으로 그렇게 하지 않습니다. 브라우저 기본값에 기대지 말고 SameSite=Lax나 Strict를 직접 지정하세요.
CSRF 예외 설정을 점검한다
프레임워크의 CSRF 보호가 일부 경로에서 꺼져 있지 않은지 확인합니다(Django의 csrf_exempt, Laravel의 예외 목록, Rails의 skip_forgery_protection 등). 웹훅 수신처럼 끌 이유가 있는 경로는 대신 서명 검증으로 보호하세요.
상태를 바꾸는 GET 경로를 찾는다
라우트 목록에서 삭제, 수정, 로그아웃을 하는 GET 경로가 없는지 살펴봅니다. 있다면 POST 등으로 옮기고 토큰 검증을 거치게 하세요.
중요한 조작에는 확인을 하나 더 둔다
비밀번호나 이메일 주소 변경, 송금, 권한 부여에는 토큰에 더해 현재 비밀번호나 다단계 인증을 요구하세요. 요청이 위조되더라도 본인 확인에서 멈춥니다.
출처(1차 자료)
- OWASP, "Cross-Site Request Forgery Prevention Cheat Sheet" — cheatsheetseries.owasp.org(토큰, Sec-Fetch-Site, SameSite의 한계, XSS에 의한 CSRF 대책 우회, GET으로 상태를 바꾸지 않기)
- MITRE, "2025 CWE Top 25 Most Dangerous Software Weaknesses"(2025년 12월) — cwe.mitre.org(CWE-352가 3위)
- web.dev, "SameSite cookies explained" — web.dev(속성이 없는 쿠키를 Lax로 취급하는 브라우저)
- INTERNET Watch, 원격 조작 악성코드 사건 보도(2012년 12월 14일, 일본어) — internet.watch.impress.co.jp(요코하마시 게시에 CSRF가 쓰였다는 경찰 설명)
- @IT, 사칭 악성코드와 CSRF로 인한 오인 체포 기사(2012년 11월, 일본어) — atmarkit.itmedia.co.jp
- IPA, "안전한 웹사이트 만드는 법"(일본어) — ipa.go.jp