[Security] CSRF란 무엇일까?

JHLee·5일 전

정보 보안

목록 보기
9/10
post-thumbnail

[Security] CSRF란 무엇일까?

1. 이 주제를 정리하게 된 이유

웹 보안 취약점은 서버나 데이터베이스에서만 발생하는 것이 아니라, 사용자의 브라우저와 요청 흐름에서도 발생할 수 있다.

로그인, 비밀번호 변경, 게시글 작성, 결제처럼 사용자의 권한으로 처리되는 기능에서는 요청이 정말 사용자의 의도에 의해 발생한 것인지 확인하는 것이 중요하다.

이때 사용자가 로그인된 상태를 악용하여, 사용자가 의도하지 않은 요청을 서버로 보내게 만드는 공격이 CSRF이다.

이번 글에서는 CSRF가 무엇인지, 왜 발생하는지, 그리고 어떻게 방어할 수 있는지 정리해보려고 한다.


2. CSRF란?

CSRFCross-Site Request Forgery의 약자로, 우리말로는 사이트 간 요청 위조라고 한다.

CSRF는 사용자가 특정 웹 서비스에 로그인된 상태를 악용하여, 사용자가 원하지 않는 요청을 서버로 보내게 만드는 공격이다.

예를 들어 사용자가 어떤 사이트에 로그인한 상태에서 공격자가 만든 악성 페이지에 접속했다고 가정해보자. 이때 악성 페이지가 해당 사이트로 이메일 변경 요청을 보내도록 만들어져 있다면, 브라우저는 로그인 쿠키를 함께 전송할 수 있다.

서버가 쿠키만 보고 정상 사용자의 요청이라고 판단하면, 사용자는 원하지 않았는데도 이메일 주소가 변경될 수 있다.

즉, CSRF는 공격자가 사용자의 비밀번호를 직접 알아내는 공격이라기보다, 이미 로그인된 사용자의 권한을 이용해 원하지 않는 요청을 실행시키는 공격이다.


3. CSRF는 왜 발생할까?

CSRF는 서버가 요청을 검증하는 방식이 부족할 때 발생한다.

웹 서비스는 사용자가 로그인하면 브라우저에 인증 쿠키를 저장한다. 이후 같은 사이트에 요청을 보낼 때 브라우저는 쿠키를 자동으로 함께 전송한다.

이 기능은 편리하지만, 서버가 단순히 “쿠키가 있으니 정상 요청이다”라고만 판단하면 문제가 생길 수 있다.

중요한 점은 인증된 사용자라는 사실과 사용자가 직접 의도한 요청이라는 사실은 다르다는 것이다.

CSRF가 발생하는 주요 원인은 다음과 같다.

  • 인증 쿠키가 요청에 자동으로 포함됨
  • 서버가 요청의 출처나 의도를 충분히 검증하지 않음
  • 중요한 기능에 추가 인증이 없음
  • CSRF Token 같은 방어 장치가 없음
  • 쿠키의 SameSite 설정이 적절하지 않음

최근 일부 모던 브라우저는 SameSite 속성이 명시되지 않은 쿠키를 Lax에 가깝게 처리한다. 이로 인해 단순한 CSRF 공격은 과거보다 어려워졌지만, 브라우저별 동작 차이나 서비스 구조에 따라 여전히 위험이 남을 수 있다. 따라서 SameSite 설정만 믿기보다는 CSRF Token 같은 서버 측 방어도 함께 적용하는 것이 안전하다.


4. 예시로 이해하기

예를 들어 어떤 사이트에 다음과 같은 기능이 있다고 가정해보자.

POST /change-email
email=attacker@example.com

이 요청은 사용자의 이메일 주소를 변경하는 요청이다.

사용자가 직접 마이페이지에서 이메일을 수정했다면 정상적인 요청이다. 하지만 공격자가 만든 악성 페이지가 사용자의 브라우저를 통해 같은 요청을 보내게 만들 수도 있다.

사용자가 해당 사이트에 로그인된 상태라면 브라우저가 인증 쿠키를 함께 전송할 수 있고, 서버는 이를 정상 요청으로 오해할 수 있다.

이처럼 CSRF는 다음과 같은 동작을 유도할 수 있다.

  • 회원 정보 변경
  • 비밀번호 변경 요청
  • 게시글 또는 댓글 작성
  • 상품 주문
  • 결제 요청

물론 실제 서비스에서는 결제나 비밀번호 변경 같은 중요한 기능에 추가 인증이 적용되는 경우가 많다. 하지만 이런 검증이 부족하면 CSRF 취약점으로 이어질 수 있다.


5. 어떻게 방어할 수 있을까?

1) CSRF Token 사용

가장 대표적인 방어 방법은 CSRF Token을 사용하는 것이다.

서버는 사용자에게 예측하기 어려운 임의의 토큰 값을 발급하고, 중요한 요청을 보낼 때 이 값을 함께 전송하도록 한다.

서버는 요청에 포함된 토큰이 올바른지 확인한 뒤 요청을 처리한다. 공격자는 사용자의 CSRF Token 값을 알기 어렵기 때문에 요청을 위조하기 어려워진다.


쿠키에 SameSite 속성을 설정하면 외부 사이트에서 발생한 요청에 쿠키가 함께 전송되는 것을 제한할 수 있다.

대표적인 값은 다음과 같다.

의미
Strict같은 사이트 요청에만 쿠키 전송
Lax외부 사이트에서 들어오더라도 최상위 이동과 GET 같은 안전한 요청 중심으로 쿠키 전송 허용
None교차 사이트 요청에도 쿠키 전송 허용. 단, Secure 설정 필요

최근 일부 브라우저는 SameSite 값을 지정하지 않은 쿠키를 Lax에 가깝게 처리하지만, 중요한 인증 쿠키에는 명시적으로 설정하는 것이 좋다.

예시는 다음과 같다.

Set-Cookie: sessionId=...; HttpOnly; Secure; SameSite=Lax

3) GET 요청으로 상태 변경을 하지 않기

정보 조회처럼 서버 상태를 변경하지 않는 요청은 GET을 사용하고, 회원 정보 변경, 삭제, 결제처럼 서버 상태를 바꾸는 요청은 POST, PUT, PATCH, DELETE 등을 사용해야 한다.

다만 POST 메서드를 사용한다고 해서 CSRF 공격이 차단되는 것은 아니다.

공격자는 악성 페이지 안에 숨겨진 <form> 태그를 만들고 JavaScript로 submit()을 강제 실행하여 POST 요청도 위조할 수 있다.

따라서 GET 대신 POST를 쓰는 것은 CSRF 자체를 막는 방어책이라기보다, GET 요청은 서버의 상태를 변경하지 않아야 한다는 HTTP 메서드의 기본 원칙을 지키기 위함에 가깝다.

즉, 서버 상태를 변경하는 기능은 GET으로 만들지 않고, CSRF Token이나 SameSite 설정과 함께 보호해야 한다.


4) Origin, Referer 헤더 검증

서버는 요청이 어떤 출처에서 왔는지 확인하기 위해 Origin 또는 Referer 헤더를 검증할 수 있다.

예를 들어 회원 정보 변경 요청이 정상 사이트가 아닌 외부 사이트에서 발생했다면 차단할 수 있다.

다만 헤더는 환경에 따라 누락될 수 있으므로 단독 방어책보다는 CSRF Token과 함께 사용하는 것이 좋다.


5) 중요한 기능에 재인증 적용

비밀번호 변경, 결제, 회원 탈퇴처럼 중요한 기능은 사용자가 다시 비밀번호를 입력하게 하거나 2차 인증을 요구할 수 있다.

이 방식은 사용자가 실제로 해당 동작을 의도했는지 확인하는 데 도움이 된다.


6) JWT 사용 시 저장 위치 주의

최근에는 세션 방식 대신 JWT(JSON Web Token)를 사용하는 서비스도 많다.

JWT를 사용할 때는 토큰을 어디에 저장하느냐에 따라 CSRF와 XSS에 대한 위험도가 달라지므로 주의해야 한다.

JWT 저장 위치CSRF 위험XSS 위험특징
LocalStorage / SessionStorage낮음토큰 탈취 위험 높음브라우저가 토큰을 자동으로 전송하지 않음. JavaScript로 직접 헤더에 실어야 함
httpOnly Cookie있음토큰 직접 탈취 위험은 낮음쿠키가 자동으로 전송되므로 세션 방식과 동일한 CSRF 방어가 필요함

핵심은 인증 정보인 JWT가 브라우저에 의해 자동으로 전송되는 구조인지이다.

JWT를 LocalStorage나 SessionStorage에 저장하면 브라우저가 토큰을 자동으로 전송하지 않기 때문에 일반적인 CSRF 위험은 낮아진다. 하지만 XSS가 발생하면 JavaScript로 토큰을 탈취당할 수 있다.

반대로 JWT를 httpOnly 쿠키에 저장하면 JavaScript로 토큰 값을 직접 읽기 어렵다. 하지만 쿠키는 요청 시 자동으로 전송되므로 CSRF 위험이 생길 수 있다.

따라서 보안을 위해 JWT를 httpOnly 쿠키에 저장한다면, 전통적인 세션 방식과 마찬가지로 CSRF Token이나 SameSite 설정을 함께 적용해야 한다.


6. 정리

CSRF는 사용자가 로그인된 상태를 악용하여, 의도하지 않은 요청을 서버로 보내게 만드는 공격이다.

이를 방어하기 위해서는 CSRF Token, SameSite Cookie 설정, Origin/Referer 검증, 재인증 등을 함께 적용하고, JWT를 사용할 때도 저장 위치에 따른 보안 위험을 고려해야 한다.

이번 글을 정리하면서 가장 중요하다고 느낀 점은 로그인된 사용자의 요청이라고 해서 항상 사용자가 의도한 요청은 아니라는 것이다.

단순히 사용자가 인증되었는지만 확인하는 것이 아니라, 그 요청이 정상적인 흐름에서 발생한 요청인지까지 함께 검증해야 한다는 점도 중요하다.

결국 보안은 하나의 기능을 추가하는 문제가 아니라, 서비스의 요청 흐름을 얼마나 신중하게 설계하느냐와 연결되어 있다. 그런 점에서 CSRF는 웹 서비스를 개발할 때 꼭 이해하고 대비해야 할 취약점이라고 느꼈다.

profile
렛츠고

0개의 댓글