CSRF

Cross-site Request Forgery

개념

사용자가 로그인해 둔 상태를 그대로 빌려 본인이 의도하지 않은 요청을 서비스에 보내게 만드는 취약점입니다.

등장 배경

브라우저에는 오래전부터 정해진 규칙이 하나 있습니다. 어떤 주소로 요청을 보낼 때 그 주소에 저장해 둔 쿠키를 자동으로 함께 실어 보내는 것입니다. 쿠키는 로그인한 뒤 서버가 브라우저에 맡겨 둔 짧은 표식이고, 이 표식이 요청에 붙어 있으면 서버는 그 요청을 로그인한 사람의 것으로 취급합니다.

이 규칙이 없으면 화면을 옮길 때마다 다시 로그인해야 하니 편의를 위한 설계입니다. 문제는 브라우저가 표식을 붙일 때 요청의 목적지만 보고 누가 그 요청을 만들었는지는 보지 않는다는 점입니다. 사용자가 버튼을 눌러 만든 요청이든 다른 사이트가 몰래 만들어 보낸 요청이든, 목적지가 같으면 같은 표식이 붙습니다.

그 틈이 사고로 드러난 방식은 오래된 게시판 이야기로 남아 있습니다. 누군가 커뮤니티 글에 보이지 않는 이미지 하나를 심어 두는데, 그 이미지의 주소가 실은 어떤 은행의 이체 요청이었습니다. 그 글을 연 사람 중 마침 그 은행에 로그인해 둔 사람은 글을 읽었을 뿐인데 이체가 실행됐습니다. 은행 서버 입장에서는 로그인한 본인이 보낸 정상 요청이었습니다.

CSRF 는 이 수법에 붙은 이름입니다. 비밀번호를 훔치거나 서버에 침입하는 공격이 아니라, 이미 열려 있는 문을 남의 손으로 미는 공격입니다.

작동 방식

공격자는 사용자의 브라우저가 대신 요청을 보내게 만들고, 브라우저는 그 요청에 로그인 표식을 자동으로 붙입니다. 순서로 풀면 다섯 단계입니다.

  1. 사용자가 페이지덱에 로그인합니다. 브라우저에 로그인 표식이 저장되고, 탭을 닫아도 보통 몇 시간에서 며칠은 유지됩니다.
  2. 사용자가 다른 사이트를 엽니다. 공격자가 만든 사이트일 수도 있고, 남이 글을 쓸 수 있는 평범한 게시판일 수도 있습니다.
  3. 그 페이지 안에 페이지덱 주소를 목적지로 하는 요청이 숨어 있습니다. 화면에 보이지 않는 입력 양식을 만들어 두고 페이지가 열리자마자 자동으로 보내는 방식이 가장 흔합니다.
  4. 브라우저가 그 요청을 보내면서 1번에서 저장해 둔 표식을 함께 붙입니다. 목적지가 페이지덱이니 규칙대로 붙이는 것입니다.
  5. 페이지덱 서버는 표식이 유효하므로 로그인한 본인의 요청으로 판단하고 처리합니다. 문서가 삭제되거나, 외부 공유가 켜지거나, 팀원 권한이 바뀝니다.

여기서 한 가지가 공격의 성격을 정합니다. 공격자는 요청을 보내게 만들 뿐 응답을 읽지는 못합니다. 브라우저가 다른 사이트에서 온 응답을 그 페이지의 코드에 넘겨주지 않기 때문입니다. 그래서 자료를 빼내는 데는 쓸모가 없고 무언가를 바꾸거나 지우는 요청에만 쓸모가 있습니다. 뒤집으면 상태를 바꾸는 요청만 지키면 된다는 뜻입니다.

막는 방법도 같은 지점을 겨냥합니다. 브라우저가 자동으로 붙여 주지 않는 무언가를 하나 더 요구하는 것입니다. 서버가 화면을 내려줄 때 예측 불가능한 문자열을 심어 두고 요청에 그 값이 같이 오는지 확인하면, 다른 사이트의 페이지는 그 값을 알 방법이 없어 걸러집니다. 쿠키에 다른 사이트에서 시작된 요청에는 붙지 말라는 표시를 다는 방법도 함께 씁니다.

쓰이는 곳

이 이름이 실제로 오가는 자리는 셋입니다.

  1. 기능을 만들 때. 상태를 바꾸는 요청을 새로 만들 때마다 방어가 걸려 있는지 확인합니다. 조회 화면에는 필요 없고 삭제, 권한 변경, 결제, 설정 저장처럼 실제로 무언가가 바뀌는 요청에 필요합니다. 요즘 웹 프레임워크는 대부분 방어를 기본으로 켜 두는데, 사고는 개발 중에 불편하다고 끈 설정이 그대로 배포되면서 납니다.
  2. 코드 리뷰와 보안 점검 때. OWASP Top 10 같은 공개 점검표에 이 항목이 들어 있어 이름이 자주 불립니다. 고객사가 도입 전에 보내는 보안 확인 문서에도 거의 빠짐없이 한 줄을 차지합니다.
  3. 인증 방식을 정할 때. 로그인 상태를 쿠키로 들고 다니면 이 문제가 따라오고, 화면 코드가 요청마다 표식을 직접 헤더에 실어 보내는 방식이면 브라우저가 자동으로 붙여 주는 것이 없으니 이 공격 자체가 성립하지 않습니다. 그래서 인증을 어떤 방식으로 할지 정하는 자리에서 이 용어가 같이 나옵니다.

인가와는 다른 층을 맡는다는 점도 짚어 둘 만합니다. 인가는 이 사람이 이 일을 해도 되는지를 보는 검사이고, 여기서는 그 검사를 통과한 사람 본인의 손을 빌리기 때문에 인가만으로는 걸러지지 않습니다.

실무 용례

"이 요청 상태를 바꾸는 건데 토큰 검사가 빠져 있어요."

다른 사이트에서 밀어 넣은 요청이 그대로 통과할 수 있다는 지적입니다.

"개발 편하려고 꺼 둔 설정인데 그대로 나갔네요."

방어 장치가 배포본에서 꺼져 있다는 뜻입니다.

"쿠키 말고 헤더로 토큰 보내면 이건 신경 안 써도 됩니다."

브라우저가 자동으로 붙이는 것이 없으니 이 공격이 성립하지 않는다는 말입니다.

페이지덱에서 이 문제가 실제로 걸린 곳은 문서 공유 설정이었습니다. 문서를 외부 공개로 바꾸는 요청이 주소만 알면 보낼 수 있는 형태로 열려 있었습니다. 태오가 점검해 보니 이 요청에만 방어가 빠져 있었고, 다른 화면보다 먼저 급하게 만들면서 프레임워크의 기본 설정을 우회한 것이 이유였습니다.

누군가 사내 게시판에 요청 하나를 심어 두면 그 글을 읽은 사람의 문서가 조용히 외부 공개로 바뀔 수 있는 상태였습니다. 그런 일이 실제로 벌어진 적은 없지만, 벌어졌다면 아무도 알아채지 못했을 것입니다. 본인이 바꾼 기록으로 남기 때문입니다.

흔한 오해

로그인 검사를 제대로 하면 막힌다고 생각하는 경우가 가장 많습니다. 누군가 남의 계정으로 들어오는 그림을 떠올리기 쉬워서입니다. 하지만 이 공격은 로그인 검사를 뚫지 않고 이미 통과한 상태를 그대로 빌립니다. 서버가 보기에 표식은 진짜고 권한도 맞습니다. 그래서 로그인 검사를 아무리 촘촘하게 해도 이 구멍은 그대로 남고, 사고가 나도 로그에는 본인이 한 일로 기록됩니다.

암호화된 연결을 쓰니 괜찮다는 오해도 흔합니다. 주소창에 자물쇠가 보이면 안전하다는 인상이 있어서입니다. 암호화는 오가는 내용을 중간에서 읽지 못하게 만드는 장치인데, 여기서 벌어지는 일은 사용자의 브라우저가 정상 경로로 요청을 보내는 것입니다. 읽을 사람이 중간에 없으니 암호화가 개입할 자리가 없습니다. 같은 이유로 방화벽이나 접속 위치 제한도 이 공격을 걸러 내지 못합니다. 요청은 언제나 사용자 본인의 기기에서 출발하기 때문입니다.

자주 헷갈리는 용어

용어무엇이 일어나는가CSRF 와의 차이
XSS공격자의 스크립트가 우리 페이지 안에서 실행됨코드가 우리 화면 안에서 돌기 때문에 응답을 읽을 수 있고, 그래서 자료를 빼내는 데까지 쓰입니다. CSRF 는 요청을 보내게 만들 뿐 결과를 보지 못합니다. 막는 방법도 달라서, 이쪽은 화면에 찍히는 값을 걸러 내는 일이 핵심입니다

같이 보면 좋은 페이지

  • 애플리케이션 보안

    이 취약점이 전체 보안 지형에서 어느 자리에 놓이는지 봅니다

  • XSS

    이름이 비슷해 자주 묶이지만 성격이 다른 취약점입니다

마지막 업데이트 2026-09-16