CSRF 공격이 발생하는 원리와 실제 피해 유형을 살펴보고 CSRF 토큰, SameSite 쿠키, Origin 검증 등을 이용한 안전한 대응 방법을 알아봅니다.
웹사이트에 로그인한 상태에서 다른 페이지의 링크를 클릭했을 뿐인데, 내 계정의 이메일 주소가 바뀌거나 원하지 않는 요청이 처리된다면 어떨까요? 사용자는 정상적인 사이트에서 직접 기능을 실행하지 않았지만, 서버에는 로그인된 사용자가 보낸 것처럼 요청이 전달될 수 있습니다.
이처럼 사용자의 인증 상태를 악용해 본인이 의도하지 않은 요청을 보내게 만드는 공격을 CSRF(Cross-Site Request Forgery)라고 합니다. 우리말로는 사이트 간 요청 위조라고 부릅니다.
CSRF 공격을 이해할 때 가장 먼저 기억해야 할 부분은 공격자가 사용자의 비밀번호를 반드시 알아야 하는 것은 아니라는 점입니다. 공격자는 이미 로그인된 사용자의 브라우저가 인증 쿠키를 자동으로 전송한다는 특성을 이용합니다.
CSRF 공격이란?
CSRF는 공격자가 만든 요청을 로그인된 사용자의 브라우저를 통해 정상적인 웹사이트로 전송하는 공격입니다. 서버는 요청에 포함된 세션 쿠키만 확인하고 사용자가 직접 실행한 요청인지까지 확인하지 않으면 해당 요청을 정상적으로 처리할 수 있습니다.
예를 들어 사용자가 쇼핑몰에 로그인한 뒤 로그아웃하지 않은 상태로 다른 웹사이트에 접속했다고 가정해 보겠습니다. 접속한 외부 페이지에 회원정보 변경 요청을 자동으로 전송하는 요소가 숨어 있다면 브라우저는 쇼핑몰에 요청을 보낼 수 있습니다.
이때 쇼핑몰의 인증 쿠키가 요청에 자동으로 포함될 수 있습니다. 서버 입장에서는 유효한 로그인 세션에서 들어온 요청처럼 보이기 때문에 별도의 검증 절차가 없다면 정보 변경을 허용할 가능성이 있습니다.
CSRF의 핵심은 다음과 같이 정리할 수 있습니다.
서버가 ‘로그인한 사용자의 요청’이라는 사실만 확인하고, ‘사용자가 의도해서 보낸 요청’인지 확인하지 않을 때 발생하는 취약점입니다.
CSRF 공격이 성립하는 조건
CSRF 공격은 모든 웹사이트에서 무조건 가능한 것이 아닙니다. 몇 가지 조건이 함께 충족돼야 합니다.
첫째, 사용자가 공격 대상 웹사이트에 로그인한 상태여야 합니다. 세션이 만료됐거나 로그아웃된 상태라면 브라우저가 유효한 인증정보를 전송하지 못하므로 공격이 정상적으로 처리되지 않을 가능성이 큽니다.
둘째, 계정정보 변경이나 게시물 작성처럼 서버의 상태를 바꾸는 기능이 존재해야 합니다. 단순히 공개된 페이지를 조회하는 기능은 CSRF로 인한 실질적인 피해가 제한적입니다.
셋째, 서버가 세션 쿠키 외에 요청의 정당성을 확인하는 값을 검사하지 않아야 합니다. CSRF 토큰, Origin 검증, 재인증과 같은 보호 절차가 적용돼 있다면 위조된 요청을 차단할 수 있습니다.
마지막으로 공격자는 요청에 필요한 파라미터와 전송 주소를 어느 정도 파악하고 있어야 합니다. 비밀번호 변경, 배송지 수정, 이메일 변경처럼 요청 형식을 예상하기 쉬운 기능이 주요 점검 대상이 되는 이유입니다.
CSRF 공격은 어떤 과정으로 진행될까?
CSRF 공격 과정을 간단한 상황으로 풀어보면 이해하기 쉽습니다.
- 사용자가 웹사이트에 정상적으로 로그인합니다.
- 서버는 로그인 상태를 유지하기 위해 세션 쿠키를 발급합니다.
- 사용자는 로그아웃하지 않은 상태로 외부 페이지나 링크에 접속합니다.
- 외부 페이지가 공격 대상 웹사이트로 특정 요청을 전송하게 합니다.
- 브라우저는 조건에 따라 저장된 세션 쿠키를 요청에 포함합니다.
- 서버가 추가 검증 없이 세션만 확인하면 요청을 정상 처리합니다.
여기서 요청을 실제로 전송하는 주체는 공격자의 서버가 아니라 피해자의 브라우저입니다. 이 때문에 대상 서버의 로그에는 정상 사용자의 세션과 IP에서 발생한 요청처럼 남을 수도 있습니다.
사용자는 외부 페이지를 열었을 뿐이지만, 브라우저 뒤에서는 계정정보 변경이나 게시물 등록 같은 요청이 처리될 수 있는 것입니다.
CSRF로 발생할 수 있는 피해
CSRF 취약점의 위험도는 대상 기능에 따라 달라집니다. 단순한 환경설정 변경이라면 영향이 크지 않을 수 있지만, 중요한 개인정보나 권한을 다루는 기능이라면 피해가 커질 수 있습니다.
대표적인 피해 유형은 다음과 같습니다.
- 회원 이메일 주소와 전화번호 변경
- 비밀번호 변경 요청
- 배송지 또는 결제 관련 정보 수정
- 사용자가 원하지 않은 게시물이나 댓글 작성
- 관리자 계정을 이용한 사용자 권한 변경
- 등록된 정보의 삭제 또는 공개 범위 변경
- 보안 설정 해제
- 특정 서비스의 신청이나 해지
특히 관리자 페이지에 CSRF 취약점이 존재하면 일반 사용자 대상 취약점보다 영향 범위가 커질 수 있습니다. 관리자가 로그인한 상태에서 조작된 페이지에 접근하는 것만으로 시스템 설정이나 사용자 권한이 변경될 가능성이 있기 때문입니다.
GET 방식이면 CSRF에 더 취약할까?
서버의 데이터를 변경하는 기능을 GET 요청으로 구현하는 것은 피해야 합니다.
GET은 원래 정보를 조회하는 용도로 사용합니다. 그런데 회원 탈퇴나 게시물 삭제처럼 상태를 변경하는 기능을 GET 주소로 제공하면 단순한 링크, 이미지 요청 또는 페이지 이동만으로도 기능이 실행될 가능성이 있습니다.
예를 들어 특정 주소에 접속하는 것만으로 게시물이 삭제되는 구조라면 공격자는 해당 주소를 이미지나 링크 형태로 외부 페이지에 포함할 수 있습니다.
그렇다고 POST 방식으로 변경하기만 하면 CSRF가 해결되는 것은 아닙니다. 외부 페이지에서도 폼을 이용해 POST 요청을 보낼 수 있기 때문입니다. 상태를 변경하는 요청에는 POST, PUT, PATCH, DELETE 등 적절한 HTTP 메서드를 사용하면서 CSRF 토큰과 같은 별도의 검증 절차를 적용해야 합니다.
CSRF 토큰을 이용한 대응 방법
가장 대표적인 CSRF 대응 방법은 CSRF 토큰을 사용하는 것입니다.
CSRF 토큰은 서버가 정상 사용자에게 발급하는 예측하기 어려운 임의의 값입니다. 사용자가 폼을 제출하거나 상태 변경 요청을 보낼 때 이 토큰을 함께 전달하고, 서버는 세션에 저장된 값과 요청에 포함된 값이 일치하는지 확인합니다.
일반적인 처리 과정은 다음과 같습니다.
- 사용자가 정상적인 웹페이지에 접속합니다.
- 서버가 사용자 세션과 연결된 CSRF 토큰을 생성합니다.
- 토큰을 HTML 폼의 숨겨진 필드나 안전한 방식으로 전달합니다.
- 사용자가 기능을 실행하면 요청과 함께 토큰이 전송됩니다.
- 서버가 토큰의 존재 여부와 일치 여부를 검사합니다.
- 토큰이 없거나 올바르지 않으면 요청을 거부합니다.
공격자는 외부 페이지에서 피해자의 세션에 연결된 올바른 토큰 값을 알기 어렵습니다. 따라서 요청 형식을 그대로 따라 하더라도 서버의 검증을 통과하지 못합니다.
토큰은 충분히 예측하기 어려워야 하며 사용자 세션과 안전하게 연결돼야 합니다. 모든 사용자에게 동일한 값이나 일정한 규칙으로 생성되는 값을 사용하면 보호 효과가 떨어집니다.
또한 CSRF 토큰을 쿠키에만 저장하고 요청 쿠키만 검사하는 방식은 적절한 방어가 되지 않을 수 있습니다. 브라우저가 위조 요청에도 쿠키를 자동으로 전송할 수 있기 때문입니다. 서버가 요청 본문이나 별도의 헤더로 전달된 토큰을 검증하도록 구성해야 합니다.
SameSite 쿠키 설정
세션 쿠키의 SameSite 속성은 다른 사이트에서 시작된 요청에 쿠키를 전송할지 제어합니다.
SameSite=Strict: 다른 사이트에서 시작된 요청에는 쿠키 전송을 강하게 제한합니다.SameSite=Lax: 일반적인 외부 링크 이동은 일부 허용하면서 여러 교차 사이트 요청에서 쿠키 전송을 제한합니다.SameSite=None: 교차 사이트 요청에도 쿠키를 전송할 수 있으며Secure속성이 함께 필요합니다.
보안만 생각하면 Strict가 강력하지만 외부 링크를 통한 로그인 유지나 연동 기능에 영향을 줄 수 있습니다. 서비스 구조와 사용자 흐름을 확인한 뒤 적절한 값을 선택해야 합니다.
SameSite 설정은 CSRF 위험을 줄이는 데 도움이 되지만, 모든 브라우저와 서비스 환경을 고려하면 이것만으로 방어를 끝내는 것은 바람직하지 않습니다. 중요한 기능에는 CSRF 토큰과 추가 인증 절차를 함께 적용하는 것이 안전합니다.
Origin과 Referer 헤더 검증
서버는 Origin 또는 Referer 헤더를 확인해 요청이 허용된 출처에서 시작됐는지 검사할 수 있습니다.
정상 서비스 도메인에서 발생한 요청만 허용하고 전혀 다른 외부 도메인에서 전달된 요청은 거부하는 방식입니다. 다만 네트워크 환경이나 개인정보 보호 설정 등에 따라 헤더가 누락될 수 있으므로 서비스 정책을 세밀하게 설계해야 합니다.
Origin과 Referer 검증은 CSRF 토큰을 보완하는 수단으로 사용하는 것이 좋습니다. 중요한 기능이라면 한 가지 검증 방식에만 의존하지 않고 여러 방어 계층을 함께 적용해야 합니다.
중요 기능에는 재인증 적용
비밀번호 변경, 출금계좌 등록, 관리자 권한 부여처럼 피해가 큰 기능은 현재 비밀번호를 다시 입력하게 하거나 추가 인증을 거치도록 구성할 수 있습니다.
공격자가 사용자의 로그인 세션을 이용하더라도 현재 비밀번호나 일회용 인증번호까지 알기는 어렵습니다. 따라서 재인증은 중요한 기능의 CSRF 피해 가능성을 크게 줄일 수 있습니다.
다만 모든 기능에 재인증을 요구하면 사용성이 지나치게 떨어질 수 있습니다. 위험도가 높은 기능을 선별해 적용하는 것이 현실적입니다.
CSRF와 CORS는 같은 보안 기능일까?
CSRF를 공부할 때 자주 생기는 오해가 하나 있습니다. CORS를 엄격하게 설정하면 CSRF도 자동으로 막을 수 있다고 생각하는 경우입니다.
CORS는 다른 출처의 웹페이지가 응답 내용을 읽을 수 있는지를 브라우저가 통제하는 정책입니다. 반면 CSRF는 피해자의 브라우저가 인증정보를 포함한 요청을 보내도록 만드는 공격입니다.
외부 사이트가 응답 내용을 읽지 못하더라도 요청 자체가 서버에 전달되고 기능이 처리될 수 있습니다. 따라서 CORS 설정만으로 CSRF 방어를 대신할 수 없습니다.
API에서는 허용된 출처를 제한하고 사용자 지정 헤더를 요구하는 방식이 도움이 될 수 있지만, 세션 쿠키를 사용하는 구조라면 CSRF 토큰과 SameSite 설정을 함께 검토해야 합니다.
XSS와 CSRF의 차이점
XSS와 CSRF는 모두 사용자의 브라우저를 이용하지만 공격 원리는 다릅니다.
XSS는 공격자가 삽입한 스크립트가 정상 웹사이트 안에서 실행되는 취약점입니다. 반면 CSRF는 정상적으로 로그인된 사용자의 권한을 이용해 의도하지 않은 요청을 보내게 하는 공격입니다.
CSRF 토큰이 적용돼 있어도 웹사이트에 XSS 취약점이 존재하면 공격 스크립트가 정상 페이지에서 토큰을 읽거나 정상 요청을 대신 실행할 가능성이 있습니다. 따라서 CSRF 방어와 함께 XSS 취약점도 반드시 제거해야 합니다.
CSRF 취약점 점검 항목
회원정보 변경이나 관리자 기능을 점검할 때는 먼저 해당 요청이 서버의 상태를 실제로 변경하는지 확인해야 합니다. 이후 다음 사항을 살펴보면 됩니다.
- 상태 변경 기능이 GET 요청으로 실행되는가?
- 요청에 CSRF 토큰이 포함되는가?
- 서버가 토큰을 실제로 검증하는가?
- 다른 사용자나 세션의 토큰을 재사용할 수 있는가?
- 토큰이 없거나 빈 값이어도 요청이 처리되는가?
- 세션 쿠키에 SameSite 속성이 설정돼 있는가?
- Origin 또는 Referer 헤더를 확인하는가?
- 비밀번호 변경 등 중요 기능에 재인증이 적용돼 있는가?
- JSON API가 단순한 폼 요청을 허용하는가?
- CSRF 방어 기능을 비활성화한 경로가 존재하는가?
점검 과정에서는 정상 요청과 비정상 요청을 구분해 서버가 어떤 조건에서 요청을 허용하는지 확인해야 합니다. 실제 서비스에 대한 테스트는 반드시 소유자의 명시적인 허가를 받은 범위에서 진행해야 합니다.
자주 묻는 질문
CSRF 공격자는 사용자의 비밀번호를 알고 있나요?
일반적으로 알 필요가 없습니다. 피해자가 이미 로그인한 상태이고 브라우저가 세션 쿠키를 자동으로 전송한다는 점을 이용하기 때문입니다.
POST 요청이면 CSRF로부터 안전한가요?
아닙니다. 외부 사이트에서도 폼을 이용해 POST 요청을 만들 수 있습니다. POST 방식과 별개로 CSRF 토큰, SameSite 쿠키, Origin 검증 등을 적용해야 합니다.
CSRF 토큰을 URL에 넣어도 되나요?
권장하지 않습니다. URL은 브라우저 방문 기록, 서버 로그, 분석 도구 또는 Referer 헤더 등에 남을 수 있습니다. 토큰은 요청 본문이나 적절한 헤더를 이용해 전달하는 것이 좋습니다.
JWT를 사용하면 CSRF가 발생하지 않나요?
JWT라는 형식 자체가 CSRF를 막아주는 것은 아닙니다. JWT를 쿠키에 저장하고 브라우저가 자동으로 전송하게 구성했다면 CSRF를 검토해야 합니다. 반면 클라이언트가 Authorization 헤더에 토큰을 직접 추가하는 구조는 일반적인 폼 기반 CSRF 위험이 줄어들지만, 토큰 저장 위치에 따른 XSS 위험은 별도로 관리해야 합니다.
마무리
CSRF는 로그인된 사용자의 브라우저를 이용해 본인이 의도하지 않은 요청을 전송하게 만드는 공격입니다. 서버가 세션 쿠키만 확인하고 요청의 출처와 의도를 검증하지 않을 때 취약점이 발생할 수 있습니다.
가장 기본적인 대응 방법은 상태 변경 요청에 예측하기 어려운 CSRF 토큰을 적용하고 서버에서 이를 반드시 검증하는 것입니다. 여기에 SameSite 쿠키, Origin·Referer 확인, 중요 기능 재인증을 함께 적용하면 방어 수준을 높일 수 있습니다.
개발 과정에서는 “로그인된 사용자가 보낸 요청인가?”에서 확인을 끝내지 말아야 합니다. “이 사용자가 우리 서비스 화면에서 직접 의도해 보낸 요청인가?”까지 확인하는 것이 CSRF 방어의 출발점입니다.
핵심 요약: CSRF는 사용자의 인증 상태를 악용하는 요청 위조 공격이며, CSRF 토큰 검증과 SameSite 쿠키, 요청 출처 확인을 함께 적용해야 합니다.
핵심 키워드: CSRF 공격, CSRF 취약점, 사이트 간 요청 위조, CSRF 토큰, SameSite 쿠키, CSRF 대응 방법
태그: 웹 보안, 웹 취약점, CSRF, 사이트 간 요청 위조, 시큐어 코딩